IOS基础使用:OC的UIKit语法

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、UIWindow与UIView
  • 二、UIControl
    • 1、UISlider
    • 2、SegmentedControl
    • 3、UISwitch
    • 4、UIActivityIndicatorView
    • 5、UIProgressView
    • 6、UIRefreshControl
  • 三、Input View
    • 1、UILabel
    • 2、UITextField
    • 3、UITextView
  • 三、UIScrollView
    • 1、创建滚动视图
    • 2、UIScrollViewDelegate
  • 四、UITableView
    • 1、PersonTableViewCell.h文件
    • 2、PersonTableViewCell.m文件
    • 3、编辑 TableView
    • 4、UITableViewDataSource
    • 5、UITableViewDelegate
    • 6、配置数据源
  • 五、UICollectionView
    • 1、通讯录cell的.h文件
    • 2、集合数据源
    • 3、UICollectionViewDataSource
    • 4、UICollectionViewDelegate
  • 六、UIPickerView
    • 1、创建选择器视图
    • 2、UIPickerViewDataSource
    • 3、UIPickerViewDelegate
  • 七、AlertView
    • 1、创建选择面板
    • 2、利用KVC方法进行UIAlertController属性的自定义
    • 3、为UIAlertController添加文本框
  • 八、UISearchBar
    • 1、UISearchBar的属性
    • 2、UISearchBar的方法
    • 3、UISearchBarDelegate代理方法
  • 九、UINavigationController
    • 1、配置返回按钮
    • 2、配置导航栏
    • 3、UITabBarController
  • 十、GestureRecognizer
    • 1、Tap: 点击
    • 2、Swipe: 轻扫
    • 3、LongPress: 长按
    • 4、Pan: 平移
    • 5、ScreenEdgePan: 屏幕边缘平移
    • 6、Pinch: 捏合
    • 7、Rotation: 旋转
    • 8、支持多个UIGestureRecongnizer共存
    • 9、响应屏幕触摸
    • 10、运动事件监听
    • 11、手势谁先响应
    • 12、手势提供的属性和方法
  • Demo
  • 参考文献

一、UIWindow与UIView

UIWindow
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
UIView
- (void)setNeedsLayout;
- (void)layoutIfNeeded;
- (void)layoutSubviews;
- (void)removeFromSuperview;
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;

self.view.subviews
[self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
CALayer
CALayer  *layer;
layer.borderWidth
layer.borderColor

二、UIControl

1、UISlider

a、创建滑条视图
- (void)createSliderView
{
    UISlider *horizontalSlider = [[UISlider alloc] initWithFrame:CGRectZero];
    
    horizontalSlider.minimumValue = 10;
    horizontalSlider.maximumValue = 100;
    [horizontalSlider addTarget:self action:@selector(horizontalSliderChanged:) forControlEvents:UIControlEventValueChanged];
    
    [self.view addSubview:horizontalSlider];
    [horizontalSlider mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
        make.height.equalTo(@44);
        make.left.equalTo(self.view.mas_left).offset(30);
        make.right.equalTo(self.view.mas_right).offset(-30);
    }];
    
    UISlider *verticalSlider = [[UISlider alloc] initWithFrame:CGRectZero];
    
    // 旋转90度
    CGAffineTransform transform = verticalSlider.transform;
    transform = CGAffineTransformRotate(transform, -M_PI/2);
    verticalSlider.transform = transform;
    
    verticalSlider.minimumValue = 10;
    verticalSlider.maximumValue = 100;
    [verticalSlider addTarget:self action:@selector(verticalSliderChanged:) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:verticalSlider];
    
    [verticalSlider mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(280);
        make.height.equalTo(@44);
        make.left.equalTo(self.view.mas_left).offset(30);
        make.right.equalTo(self.view.mas_right).offset(-30);
    }];
}
b、事件方法
- (void)horizontalSliderChanged:(id)sender
{
    UISlider *slider = sender;
    NSLog(@"水平滑条值改变:%f", slider.value);
}

- (void)verticalSliderChanged:(id)sender
{
    UISlider *slider = sender;
    NSLog(@"垂直滑条值改变: %f", slider.value);
}
c、输出结果
2020-10-13 15:36:17.451947+0800 BasicGrammarDemo[45225:2120947] 水平滑条值改变:58.211006
2020-10-13 15:36:17.822776+0800 BasicGrammarDemo[45225:2120947] 水平滑条值改变:58.211006
2020-10-13 15:36:18.509124+0800 BasicGrammarDemo[45225:2120947] 垂直滑条值改变: 10.000000
2020-10-13 15:36:18.530712+0800 BasicGrammarDemo[45225:2120947] 垂直滑条值改变: 12.110093

2、SegmentedControl

a、创建SegmentedControl视图
- (void)createSegmentedControl
{
    UISegmentedControl *segmentControl = [[UISegmentedControl alloc] initWithItems:@[@"item1", @"item2", @"item3"]];
    
    segmentControl.selectedSegmentIndex = 1;
    
    [segmentControl addTarget:self action:@selector(segmentControlChanged:) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:segmentControl];
    
    [segmentControl mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(60);
        make.height.equalTo(@44);
        make.left.equalTo(self.view.mas_left).offset(20);
        make.right.equalTo(self.view.mas_right).offset(-100);
    }];
}
b、事件方法
- (void)segmentControlChanged:(id)sender
{
    UISegmentedControl *segmentControl = sender;
    NSLog(@"segmentControl值改变:%@", @(segmentControl.selectedSegmentIndex));
}
c、输出结果
2020-10-13 15:35:23.543024+0800 BasicGrammarDemo[45161:2117200] segmentControl值改变:0

3、UISwitch

a、创建开关视图
- (void)createSwitchView
{
    UISwitch *switcher = [[UISwitch alloc] initWithFrame:CGRectZero];
    [switcher addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:switcher];
    
    [switcher mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(120);
        make.height.equalTo(@44);
        make.left.equalTo(self.view.mas_left).offset(20);
        make.width.equalTo(@100);
    }];
}
b、输出结果
2020-10-13 16:06:16.290280+0800 BasicGrammarDemo[45707:2145882] 开关改变了:1

4、UIActivityIndicatorView

- (void)createActivityIndicatorView
{
    UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
    
    // 只能设置中心,不能设置大小
    activityIndicator.center = CGPointMake(200.0f, 200.0f);
    // 改变圈圈的颜色为红色
    activityIndicator.color = [UIColor redColor];
    
    // 开始旋转
    [activityIndicator startAnimating];
    // 结束旋转
    [activityIndicator stopAnimating];
    
    // 添加指示器到导航栏
    self.navigationItem.titleView = activityIndicator;
    self.navigationItem.prompt =  @"正在使出吃奶的劲加载中...";
    
    // 停止指示器控件本应该调用 stopAnimating 方法,但放在导航栏项目中的活动指示器控件有所不同
    // 要移除这个控件,让原来的title内容显示出来
    self.navigationItem.titleView = nil;
    self.navigationItem.prompt = nil;
    
    [self.view addSubview:activityIndicator];
}

5、UIProgressView

self.progressView.progress = self.progressView.progress + 0.1;
if (self.progressView.progress == 1.0)
{
    [self.timer invalidate];
}

6、UIRefreshControl

self.collectionView.alwaysBounceVertical = YES;// 必须打开
self.refreshControl.tintColor = [UIColor grayColor];
[self.refreshControl addTarget:self action:@selector(urlRequestForCollection) forControlEvents:UIControlEventValueChanged];
[self.collectionView addSubview:self.refreshControl];
[weakSelf.refreshControl endRefreshing];// 结束刷新

三、Input View

1、UILabel

UILabel
- (void)createLabelView
{
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(150, 100, 100, 50)];

    // 位置位于视图中央
    label.center = self.view.center;

    // 自适应大小的方法,即标签的大小由字体的大小长度决定
    // 这个打开后会让label不再显示,慎用!!!
    // [label sizeToFit];

    // 设置字体大小是否适应label宽度
    label.adjustsFontSizeToFitWidth = YES;

    // 设置label的换行模式为根据单词进行换行
    label.lineBreakMode = NSLineBreakByWordWrapping;
    
    // 设置label显示几行,0表示可以有无限行
    label.numberOfLines = 0;

    // 设置文本是否高亮和高亮时的颜色
    label.highlighted = YES;
    label.highlightedTextColor = [[UIColor orangeColor] colorWithAlphaComponent:0.5];
    
    // 设置阴影的颜色和阴影的偏移位置
    label.shadowColor = [UIColor redColor];
    label.shadowOffset = CGSizeMake(1.0, 1.9);
    
    // 设置label中的文字是否可变,默认值是YES
    label.enabled = YES;

    // 如果adjustsFontSizeToFitWidth属性设置为YES,这个属性就来控制文本基线的行为
    // 将文本缩小到基于原始基线的位置时使用,默认不使用
    label.baselineAdjustment = UIBaselineAdjustmentNone;
    
    // 设置背景色为透明
    label.backgroundColor = [UIColor clearColor];
    
    // 设置label的旋转角度
    label.transform = CGAffineTransformMakeRotation(0.2);
    
    // 让label自适应里面的文字,自动调整宽度和高度
    NSString *string = @"天地之间隐有梵音";
    [string boundingRectWithSize:CGSizeMake(320, 2000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil];
    
    // 让UILabel具有链接功能
    // 如果我并没有安装app那它就会失败,同时会调用safari来打开这个链接
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http:baidu.com"] options:@{UIApplicationOpenURLOptionUniversalLinksOnly : @YES} completionHandler:nil];
    
    // 让label显示出来
    label.backgroundColor = [UIColor blackColor];
    label.text = string;
    [self.view addSubview:label];
}

2、UITextField

a、创建textField视图
- (void)createTextFieldView
{
    self.passwordTextField = [[UITextField alloc] initWithFrame:CGRectZero];
    
    self.passwordTextField.borderStyle = UITextBorderStyleRoundedRect;// 圆角矩形
    self.passwordTextField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 22, 22)];// 左视图大小
    self.passwordTextField.leftView.backgroundColor = [UIColor redColor];// 左视图背景颜色
    self.passwordTextField.leftViewMode = UITextFieldViewModeAlways;// 左视图总是显示
    self.passwordTextField.clearButtonMode = UITextFieldViewModeWhileEditing;// 显示清空按钮
    self.passwordTextField.secureTextEntry = YES;// 加密显示,该属性通常用于设置密码输入框
    self.passwordTextField.clearsOnBeginEditing = YES;// 当重复开始编辑时候清除文字
    //  返回类型:Default换行 Go前往 Google/Search搜索 Join加入 Next下一项 Route路线 Send发送 Done完成 Continue继续
    self.passwordTextField.returnKeyType = UIReturnKeySearch;
    // 设置弹出视图inputView,即弹出的不是键盘而是这个视图
    // 默认的整个键盘宽度,只有设置高度会有用
    self.passwordTextField.inputView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"luckcoffee.JPG"]];
    // 纵向对齐方式,默认是居中
    self.passwordTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    // 设置调整文字大小以适配宽度(即输入不下时缩小文字,实在缩小不了了,就向后滚动),默认是向右滚动的
    self.passwordTextField.adjustsFontSizeToFitWidth = YES;
    // 设置最小字号,和上面有关,即小于这个字号的时候就不缩小了,直接向右滚动
    self.passwordTextField.minimumFontSize = 2;
    // 设置字母大小样式 AllCharacters-所有字母大写 Words-单词首字母大写 Sentences-句首字母大写
    self.passwordTextField.autocapitalizationType = UITextAutocapitalizationTypeSentences;

    self.passwordTextField.delegate = self;
    [self.passwordTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

    [self.view addSubview:self.passwordTextField];
    [self.passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(60);
        make.height.equalTo(@44);
        make.left.equalTo(self.view.mas_left).offset(20);
        make.right.equalTo(self.view.mas_right).offset(-20);
    }];
}
b、UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField// 是否可以编辑
{
    return YES;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField// 开始编辑
{
    NSLog(@"开始编辑");
}

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField// 是否应该结束编辑
{
    return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField// 结束编辑
{
    NSLog(@"结束编辑");
}

- (void)textFieldDidChange:(UITextField *)textField// 获取编辑完成的文本
{
    NSLog(@"获取编辑完成的文本 %@", textField.text);
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string// 编辑过程中会不断调用
{
    if (string.length > 0)// 有输入字符
    {
        if ([string isEqualToString:@"0"])// 输入0则允许改变,可以继续输入
        {
            return YES;
        }
        return NO;
    }
    return YES;
}

- (BOOL)textFieldShouldClear:(UITextField *)textField// 是否可以清空
{
    // 点击清除按钮,应该同时将本地数据源里的文本清空
    if (textField == self.passwordTextField)
    {
        self.password = @"";
    }
    return YES;
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField// 是否可以返回
{
    return YES;
}
输出结果
2020-10-13 16:09:15.357943+0800 BasicGrammarDemo[45707:2145882] 开始编辑
2020-10-13 16:09:16.478273+0800 BasicGrammarDemo[45707:2145882] 获取编辑完成的文本 0
2020-10-13 16:09:16.643681+0800 BasicGrammarDemo[45707:2145882] 获取编辑完成的文本 00

3、UITextView

a、创建textView视图
- (void)createTextView
{
    self.textView = [[UITextView alloc] initWithFrame:CGRectZero];
    
    self.textView.layer.borderColor = [UIColor blackColor].CGColor;
    self.textView.layer.borderWidth = 1.0;
    
    self.textView.delegate = self;
    self.textView.text = @"孟子,既然男女授受不亲,嫂子掉到水里,要不要伸手去拉,假如“礼”是那么重要,人命就不要了吗?,不去救嫂子则“是豺狼也”,但这要怪嫂子干吗要掉进水里。他没有说戴上了手套再去拉嫂子,唯一的不幸就是自己的无能,对数学家来说,只要他能证明费尔马定理,就可以获得全球数学家的崇敬,自己也可以得到极大的快感,问题在于你证不出来。";
    
    [self.view addSubview:self.textView];
    [self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(120);
        make.height.equalTo(@100);
        make.left.equalTo(self.view.mas_left).offset(20);
        make.right.equalTo(self.view.mas_right).offset(-20);
    }];
}
b、UITextViewDelegate
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView// 是否可以编辑
{
    return YES;
}

- (BOOL)textViewShouldEndEditing:(UITextView *)textView// 是否应该结束编辑
{
    return YES;
}

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    NSLog(@"开始编辑");
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
    NSLog(@"结束编辑");
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text// 编辑过程中会不断调用
{
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView// 获取编辑完成的文本
{
    NSLog(@"获取编辑完成的文本 %@", textView.text);
}

- (void)textViewDidChangeSelection:(UITextView *)textView// 编辑完成的文本
{
    NSLog(@"改变了选中部分的文本");
}
输出结果
2020-10-13 17:25:36.642017+0800 BasicGrammarDemo[55396:2252994] 开始编辑
2020-10-13 17:25:36.645211+0800 BasicGrammarDemo[55396:2252994] 改变了选中部分的文本
2020-10-13 17:25:47.733527+0800 BasicGrammarDemo[55396:2252994] 获取编辑完成的文本 孟子,既然男女授受不亲,嫂子掉到水里,要不要伸手去拉,假如“礼”

三、UIScrollView

1、创建滚动视图

- (void)createScrollView
{
    self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
    self.scrollView.delegate = self;
    
    self.scrollView.pagingEnabled = YES;// 是否支持翻页
    self.scrollView.showsHorizontalScrollIndicator = YES;// 展示水平滚动条
    self.scrollView.showsVerticalScrollIndicator = NO;// 展示垂直滚动条
    self.scrollView.minimumZoomScale = 0.5;// 最小化
    self.scrollView.maximumZoomScale = 2.0;// 最大化
    self.scrollView.scrollsToTop = YES;// 要不要返回顶部
    self.scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;// 滚动条颜色
    CGSize viewSize = self.view.bounds.size;
    self.scrollView.contentSize = CGSizeMake(viewSize.width*3, viewSize.height);// 内容为屏幕3倍用于横向滑动
    
    [self.view addSubview:self.scrollView];
    [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
        make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
        make.left.equalTo(self.view.mas_left);
        make.right.equalTo(self.view.mas_right);
    }];

    self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectZero];
    
    self.pageControl.numberOfPages = 3;// 3页
    self.pageControl.pageIndicatorTintColor = [UIColor colorWithWhite:1 alpha:0.5];// 小点的颜色
    self.pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];// 当前点的颜色
    
    [self.view addSubview:self.pageControl];
    [self.pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(-20);
        make.height.equalTo(@44);
        make.left.equalTo(self.view.mas_left);
        make.right.equalTo(self.view.mas_right);
    }];

    // 创建红、蓝、绿三个页面
    UIView *redView = [self createScrollViewSubViewWithOffset:0];
    redView.backgroundColor = [UIColor redColor];
    
    UIView *blueView = [self createScrollViewSubViewWithOffset:viewSize.width];
    blueView.backgroundColor = [UIColor blueColor];
    
    UIView *greenView = [self createScrollViewSubViewWithOffset:viewSize.width*2];
    greenView.backgroundColor = [UIColor greenColor];
}

- (UIView *)createScrollViewSubViewWithOffset:(CGFloat)offsetX
{
    CGSize viewSize = self.view.bounds.size;
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(offsetX, 0, viewSize.width, viewSize.height)];
    [self.scrollView addSubview:view];
    return view;
}

2、UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView// 滚动了
{
    NSLog(@"偏移量:%@", @(scrollView.contentOffset.x));
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView// 结束滚动
{
    CGFloat offsetX = scrollView.contentOffset.x;
    NSLog(@"结束滚动时候在X轴的偏移量:%@", @(offsetX));
    
    // 计算当前页数
    NSInteger index = offsetX / scrollView.frame.size.width;
    self.pageControl.currentPage = index;
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView// 需要缩放的视图
{
    CGFloat offsetX = scrollView.contentOffset.x;
    NSInteger index = offsetX / scrollView.frame.size.width;
    return scrollView.subviews[index];
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView// 缩放比例
{
    NSLog(@"缩放了 %@", @(scrollView.zoomScale));
}
输出结果
2020-10-13 17:43:22.163863+0800 BasicGrammarDemo[55573:2261834] 缩放了 0.7947149384108813
2020-10-13 17:43:22.241076+0800 BasicGrammarDemo[55573:2261834] 偏移量:-23.19056305867597
2020-10-13 17:45:23.062048+0800 BasicGrammarDemo[55573:2261834] 结束滚动时候在X轴的偏移量:228.3333333333333

四、UITableView

1、PersonTableViewCell.h文件

@interface PersonTableViewCell : UITableViewCell

@property (nonatomic, strong, readonly) UILabel *nameLabel;// 姓名
@property (nonatomic, strong, readonly) UILabel *namePinyinLabel;// 姓名拼音
@property (nonatomic, strong, readonly) UILabel *mobileLabel;// 手机
@property (nonatomic, strong, readonly) UILabel *introductionLabel;// 介绍

@end

2、PersonTableViewCell.m文件

a、初始化方法
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self)
    {
        [self createSubViews];
        [self createSubViewsConstraints];
    }
    return self;
}
b、添加子视图
- (void)createSubViews
{
    self.nameLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    self.nameLabel.textAlignment = NSTextAlignmentLeft;
    [self.contentView addSubview:self.nameLabel];
    
    self.namePinyinLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    self.namePinyinLabel.textAlignment = NSTextAlignmentRight;
    [self.contentView addSubview:self.namePinyinLabel];
    
    self.mobileLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    [self.contentView addSubview:self.mobileLabel];
    
    self.introductionLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    self.introductionLabel.numberOfLines = 0;
    [self.contentView addSubview:self.introductionLabel];
}
c、添加约束
- (void)createSubViewsConstraints 
{
    [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.contentView.mas_top).offset(10);
        make.height.equalTo(@30);
        make.left.equalTo(self.contentView.mas_left).offset(20);
        make.right.equalTo(self.contentView.mas_right).offset(-20);
    }];
    
    [self.namePinyinLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.nameLabel.mas_top);
        make.bottom.equalTo(self.nameLabel.mas_bottom);
        make.left.equalTo(self.contentView.mas_left).offset(20);
        make.right.equalTo(self.contentView.mas_right).offset(-20);
    }];
    
    [self.mobileLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.nameLabel.mas_bottom).offset(10);
        make.height.equalTo(@30);
        make.left.equalTo(self.contentView.mas_left).offset(20);
        make.right.equalTo(self.contentView.mas_right).offset(-20);
    }];
    
    [self.introductionLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.mobileLabel.mas_bottom).offset(10);
        make.bottom.equalTo(self.contentView.mas_bottom).offset(-10);
        make.left.equalTo(self.contentView.mas_left).offset(20);
        make.right.equalTo(self.contentView.mas_right).offset(-20);
    }];
}

3、编辑 TableView

a、为导航栏添加编辑按钮
- (void)configureNavigationbar
{
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"编辑" style:UIBarButtonItemStylePlain target:self action:@selector(editTable)];
    
    self.editFinished = YES;
}
b、点击导航栏的编辑按钮
- (void)editTable
{
    if (self.isEditFinished)
    {
        self.editFinished = NO;
        self.navigationItem.rightBarButtonItem.title = @"完成";
        self.tableView.editing = YES;// 正在编辑
    }
    else
    {
        self.editFinished = YES;
        self.navigationItem.rightBarButtonItem.title = @"编辑";
        self.tableView.editing = NO;// 非编辑状态
    }
}
c、创建TableView
- (void)createTableView
{
    self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
    [self.tableView registerClass:[PersonTableViewCell class] forCellReuseIdentifier:TableCellReuseIdentifier];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    
    // 不显示右侧滑块
    self.tableView.showsVerticalScrollIndicator = NO;
    // 分割线
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
    // 表头表尾高度
    self.tableView.sectionHeaderHeight = 20;
    self.tableView.sectionFooterHeight = 20;
    // 弹动
    self.tableView.bounces = NO;
    
    // 多选模式
//    self.tableView.allowsSelection = YES;
//    self.tableView.allowsSelectionDuringEditing = YES;
//    self.tableView.allowsMultipleSelection = YES;
//    self.tableView.allowsMultipleSelectionDuringEditing = YES;
    
    [self.view addSubview:self.tableView];
    [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
        make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
        make.left.equalTo(self.view.mas_left);
        make.right.equalTo(self.view.mas_right);
    }];
}

4、UITableViewDataSource

a、section 数量
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.personTableArray.count;
}
b、cell 数量
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.personTableArray[section].count;
}
c、cell 样式
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    PersonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableCellReuseIdentifier forIndexPath:indexPath];
    
    cell.selectionStyle = UITableViewCellSelectionStyleNone;// 选中风格
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;// 右指示器
    //cell.accessoryView = imageBtn;// cell可以设置装饰图
    
    // cell自带imageView属性、text属性、detailTextLabel属性
    cell.detailTextLabel.text = @"更多";
    
    PersonModel *personModel = self.personTableArray[indexPath.section][indexPath.row];
    cell.nameLabel.text = personModel.name;
    cell.namePinyinLabel.text = personModel.namePinyin;
    cell.mobileLabel.text = personModel.mobile;
    cell.introductionLabel.text = personModel.introduction;
    
    return cell;
}
d、SectionHeader 标题
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return self.sectionIndexTitles[section];// 索引数组
}
e、能否编辑 cell
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return (indexPath.section > 0);// 除第一节外都可编辑
}
f、能否移动 cell
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return (indexPath.section == 1);// 第二节可以移动cell
}
g、TableView的右边索引列的标题
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    return self.sectionIndexTitles;
}
f、TableView的右边索引列的位置,好像没什么用
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    return index;
}
g、支持插入和删除 cell
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        // 从数据源里移除当前cell的数据
        NSMutableArray *personArray = self.personTableArray[indexPath.section];
        [personArray removeObjectAtIndex:indexPath.row];

        // 移除完后当前Section不再拥有cell了
        if (personArray.count == 0)
        {
            // 移除当前Section的数据和索引标题
            [self.personTableArray removeObjectAtIndex:indexPath.section];
            [self.sectionIndexTitles removeObjectAtIndex:indexPath.section];
        }

        // 从视图中移除当前cell
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
}
h、支持移动 cell
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    // 从数据源中移除初始cell的数据
    NSMutableArray *sourcePersonArray = self.personTableArray[sourceIndexPath.section];
    PersonModel *sourcePersonModel = sourcePersonArray[sourceIndexPath.row];
    [sourcePersonArray removeObjectAtIndex:sourceIndexPath.row];
    self.personTableArray[sourceIndexPath.section] = sourcePersonArray;

    // 从数据源中向目的cell添加数据
    NSMutableArray *destinationPersonArray = self.personTableArray[destinationIndexPath.section];
    [destinationPersonArray insertObject:sourcePersonModel atIndex:destinationIndexPath.row];
    self.personTableArray[destinationIndexPath.section] = destinationPersonArray;
}

5、UITableViewDelegate

a、选中 cell
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"didSelectRowAtIndexPath %@", indexPath);
}
b、编辑风格
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleDelete;
}
c、删除确认按钮的标题
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return @"删除";
}

6、配置数据源

- (void)readTablePersonData
{
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"person" withExtension:@"json"];
    NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:NSDataReadingMappedIfSafe error:nil];
    NSArray<NSDictionary *> *persons = [NSJSONSerialization JSONObjectWithData:fileData options:NSJSONReadingMutableContainers error:nil];
    
    // json to model
    NSMutableArray<PersonModel *> *personModelArray = [NSMutableArray array];
    [persons enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PersonModel *personModel = [[PersonModel alloc] init];
        personModel.name = obj[@"name"];
        personModel.namePinyin = obj[@"pinyin"];
        personModel.nameFirstLetter = obj[@"first_letter"];
        personModel.mobile = obj[@"mobile"];
        personModel.introduction = obj[@"introduction"];
        
        [personModelArray addObject:personModel];
    }];
    
    // 按照字母顺序进行排序
    [personModelArray sortUsingComparator:^NSComparisonResult(PersonModel *obj1, PersonModel *obj2) {
        return [obj1.namePinyin compare:obj2.namePinyin];
    }];
    
    // 按拼音首字母拆分,顺序遍历
    NSMutableArray<NSMutableArray<PersonModel *> *> *personDataArray = [NSMutableArray array];
    NSMutableArray<NSString *> *sectionIndexArray = [NSMutableArray array];
    NSMutableArray<PersonModel *> *personArray = [NSMutableArray array];
    
    NSInteger count = personModelArray.count;
    for (int i=0; i<count; I++)
    {
        PersonModel *personModel = personModelArray[I];
        if (![sectionIndexArray containsObject:personModel.nameFirstLetter])// 不包含的首字母则需要新的array容纳
        {
            // 将该首字母添加到索引数组
            [sectionIndexArray addObject:personModel.nameFirstLetter];
            if (personArray.count > 0)// array代表的上一个section存在model
            {
                // personDataArray代表[Section[row]]
                [personDataArray addObject:[personArray mutableCopy]];
                // 清空旧的model,成为新array
                [personArray removeAllObjects];
            }
        }
        // 首字母已经被包含,说明是该model处于同一个section,则添加到array即可
        [personArray addObject:personModel];
    }
    // 添加最后一个personArray
    [personDataArray addObject:[personArray copy]];
    
    // 重新加载table
    self.personTableArray = personDataArray;
    self.sectionIndexTitles = sectionIndexArray;
    [self.tableView reloadData];
}

五、UICollectionView

NSString * const CollectionCellReuseIdentifier = @"cell";

@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

// 数据源
@property (nonatomic, strong) NSArray *personArray;

// 集合视图
@property (nonatomic, strong) UICollectionView *collectionView;

@end

1、创建集合视图

- (void)createCollectionView
{
    // 创建集合视图的布局
    CGSize viewSize = self.view.bounds.size;
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(viewSize.width/2 - 10, viewSize.height);
    layout.minimumLineSpacing = 10;// Item之间的上下间距
    layout.minimumInteritemSpacing = 10;// Item之间的左右间距
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;// 滚动方向
    
    // 创建集合视图
    self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
    self.collectionView.backgroundColor = [UIColor whiteColor];// 默认没有颜色,会渲染成黑色
    [self.collectionView registerClass:[PersonCollectionViewCell class] forCellWithReuseIdentifier:CollectionCellReuseIdentifier];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.view addSubview:self.collectionView];
    [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
        make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
        make.left.equalTo(self.view.mas_left);
        make.right.equalTo(self.view.mas_right);
    }];
}

2、集合数据源

- (void)readPersonData
{
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"person" withExtension:@"json"];
    NSData *fileData = [NSData dataWithContentsOfURL:fileURL options:NSDataReadingMappedIfSafe error:nil];
    NSArray<NSDictionary *> *persons = [NSJSONSerialization JSONObjectWithData:fileData options:NSJSONReadingMutableContainers error:nil];
    
    // json to model
    NSMutableArray<PersonModel *> *personArray = [NSMutableArray array];
    [persons enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        PersonModel *personModel = [[PersonModel alloc] init];
        personModel.name = obj[@"name"];
        personModel.namePinyin = obj[@"pinyin"];
        personModel.nameFirstLetter = obj[@"first_letter"];
        personModel.mobile = obj[@"mobile"];
        personModel.introduction = obj[@"introduction"];
        
        [personArray addObject:personModel];
    }];
    
    // 按照字母顺序进行排序
    [personArray sortUsingComparator:^NSComparisonResult(PersonModel *obj1, PersonModel *obj2) {
        return [obj1.namePinyin compare:obj2.namePinyin];
    }];
    
    // 重新加载页面
    self.personArray = personArray;
    [self.collectionView reloadData];
}

3、UICollectionViewDataSource

Item数目
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.personArray.count;
}
Item内容
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    PersonCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CollectionCellReuseIdentifier forIndexPath:indexPath];
    
    PersonModel *personModel = self.personArray[indexPath.item];
    cell.nameLabel.text = personModel.name;
    cell.namePinyinLabel.text = personModel.namePinyin;
    cell.mobileLabel.text = personModel.mobile;
    cell.introductionLabel.text = personModel.introduction;
    
    return cell;
}

4、UICollectionViewDelegate

选中Item
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"选中了Item");
}

5、UICollectionViewDelegateFlowLayout

Item尺寸大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 页面宽度一半 - 10 作为Item宽度
    CGSize viewSize = self.view.bounds.size;
    CGFloat width = viewSize.width/2 - 10;
    
    // 根据字号、宽度、内容来估计introduction的高度
    PersonModel *personModel = self.personArray[indexPath.row];
    CGSize size = [personModel.introduction boundingRectWithSize:CGSizeMake(width - 24, 2000) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:15.0]} context:nil].size;
    
    // 返回Item尺寸大小
    return CGSizeMake(width, size.height + 160);
}

六、UIPickerView

1、创建选择器视图

- (void)createPickerView
{
    self.pickerView = [[UIPickerView alloc] initWithFrame:CGRectZero];
    self.pickerView.delegate = self;
    self.pickerView.dataSource = self;
    [self.view addSubview:self.pickerView];
    [self.pickerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
        make.height.equalTo(@200);
        make.left.equalTo(self.view.mas_left);
        make.right.equalTo(self.view.mas_right);
    }];
    
    self.datePicker = [[UIDatePicker alloc] initWithFrame:CGRectZero];
    [self.datePicker addTarget:self action:@selector(datePickerChanged:) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:self.datePicker];
    [self.datePicker mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop).offset(300);
        make.height.equalTo(@200);
        make.left.equalTo(self.view.mas_left);
        make.right.equalTo(self.view.mas_right);
    }];
}

- (void)datePickerChanged:(id)sender
{
    UIDatePicker *datePicker = sender;
    NSLog(@"选择的日期为:%@", datePicker.date);
}

2、UIPickerViewDataSource

转盘数目
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 2;
}
每个转盘的行数
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    return 5;
}

3、UIPickerViewDelegate

a、每个转盘的宽度
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
{
    return self.view.bounds.size.width/2;
}
b、转盘中每一行的高度
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component
{
    return 44.0;
}
c、每一行的标题
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    return self.pickerData[component][row];
}
d、选择转盘
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    NSLog(@"选择的转盘为:%@, 选择的行数上的内容为:%@", @(component), self.pickerData[component][row]);
    if (component == 0)// 滚动第一个转盘时候需要联动第二个转盘
    {
        [pickerView reloadComponent:1];
    }
}
e、数据源
- (void)configuePickerData
{
    NSArray *columnFirst = @[@"谢佳培", @"王德胜", @"白落梅", @"车行迟", @"林风眠"];
    NSArray *columnSecond = @[@"IOS开发", @"院士", @"作家", @"虚构", @"画家"];
    self.pickerData = @[columnFirst, columnSecond];
    
    // 重新加载表盘
    [self.pickerView reloadAllComponents];
}
输出结果
2020-10-13 18:12:46.186157+0800 BasicGrammarDemo[56013:2286395] 选择的转盘为:1, 选择的行数上的内容为:IOS开发

七、AlertView

1、创建选择面板

- (void)showActionSheetController
{
    UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:@"作家" message:@"每个人的名字都像一幅水墨画" preferredStyle:UIAlertControllerStyleActionSheet];
    
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"点击了取消");
    }];
    [actionSheetController addAction:cancelAction];
    
    UIAlertAction *firstAction = [UIAlertAction actionWithTitle:@"查看白落梅" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"查看白落梅");
    }];
    [actionSheetController addAction:firstAction];
    
    UIAlertAction *secondAction = [UIAlertAction actionWithTitle:@"查看丰子恺" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"查看丰子恺");
    }];
    [actionSheetController addAction:secondAction];
    
    UIAlertAction *thirdAction = [UIAlertAction actionWithTitle:@"查看郁达夫" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"查看郁达夫");
    }];
    [actionSheetController addAction:thirdAction];
    
    [self presentViewController:actionSheetController animated:YES completion:nil];
}

2、利用KVC方法进行UIAlertController属性的自定义

- (void)customAlert
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"提示内容" preferredStyle:UIAlertControllerStyleAlert];
    
    // 修改title
    NSMutableAttributedString *alertControllerStr = [[NSMutableAttributedString alloc] initWithString:@"提示"];
    [alertControllerStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 2)];
    [alertControllerStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, 2)];
    // 利用KVC方法进行UIAlertController属性的自定义
    // 有时候使用第三方控件会带来很多不必要的代码量和bug,所以能用系统自带最好
    [alertController setValue:alertControllerStr forKey:@"attributedTitle"];
    
    // 修改message
    NSMutableAttributedString *alertControllerMessageStr = [[NSMutableAttributedString alloc] initWithString:@"提示内容"];
    [alertControllerMessageStr addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(0, 4)];
    [alertControllerMessageStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20] range:NSMakeRange(0, 4)];
    [alertController setValue:alertControllerMessageStr forKey:@"attributedMessage"];
    
    // 常规按钮
    UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"Default" style:UIAlertActionStyleDefault handler:nil];
    // 销毁按钮
    UIAlertAction *destructiveAction = [UIAlertAction actionWithTitle:@"Destructive" style:UIAlertActionStyleDestructive handler:nil];
    // 取消按钮
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
    
    [alertController addAction:defaultAction];
    [alertController addAction:destructiveAction];
    [alertController addAction:cancelAction];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

3、为UIAlertController添加文本框

- (void)textFieldForAlert
{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
    
    // 添加文本框
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        // 设置键盘输入为数字键盘
        textField.keyboardType = UIKeyboardTypeNumberPad;
        textField.placeholder = @"请填写";
    }];
    
    // 添加取消按钮
    UIAlertAction *cancelButton = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        // 取消
    }];
    [alert addAction: cancelButton];
        
    // 添加确定按钮
    UIAlertAction *confirmButton = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        // 文本框结束编辑,收起键盘
        [[alert.textFields firstObject] endEditing:YES];
        // 获取文本框填写的内容
        NSString *meetingId = [alert.textFields firstObject].text;
        if (meetingId.length > 12)
        {
            //[weakSelf showText:@"会议号过长"];
        }
        else
        {
            //[weakSelf enterVideoMeeting:meetingId];
        }
    }];
    [alert addAction: confirmButton];
    
    //显示
    [self presentViewController:alert animated:YES completion:nil];
}

八、UISearchBar

1、UISearchBar的属性

// 搜索框风格 
// UIBarStyleDefault 白色搜索框,灰色背景
// UIBarStyleBlack 黑色搜索框
@property(nonatomic) UIBarStyle barStyle;

 // 搜索的文本
@property(nullable,nonatomic,copy)   NSString *text;  
 // 搜索框顶部的提示信息 
@property(nullable,nonatomic,copy)   NSString *prompt;    
// 占位符,默认nil, 若有值则在输入文本后消失
@property(nullable,nonatomic,copy)   NSString *placeholder;  

// 搜索框右侧是否显示图书按钮 
@property(nonatomic)  BOOL showsBookmarkButton;   📖
//搜索框右侧是否显示取消按钮 
@property(nonatomic) BOOL showsCancelButton;  Cancel
//搜索框右侧是否显示搜索结果按钮   
@property(nonatomic) BOOL showsSearchResultsButton; ⤓
// 搜索结果按钮为选中状态
@property(nonatomic, getter=isSearchResultsButtonSelected) BOOL searchResultsButtonSelected;☑️

// 风格颜色,可用于修改输入框的光标颜色,取消按钮和选择栏被选中时候都会变成设置的颜色
@property(null_resettable, nonatomic,strong) UIColor *tintColor; //|线条的颜色
// 搜索框背景颜色
@property(nullable, nonatomic,strong) UIColor *barTintColor;

// 搜索框样式
// UISearchBarStyleMinimal:不显示背景,系统自带的背景色无效,自定义的有效,常用在Calendar, Notes and Music
// UISearchBarStyleProminent、UISearchBarStyleDefault:显示背景,常用在my Mail, Messages and Contacts
@property (nonatomic) UISearchBarStyle searchBarStyle

// 选择按钮视图的按钮标题    
@property(nullable, nonatomic,copy) NSArray<NSString *> *scopeButtonTitles ; 
// 选中的按钮下标值 ,默认 0. 如果超出范围则忽略
@property(nonatomic) NSInteger  selectedScopeButtonIndex ; 
// 是否显示搜索栏的附件选择按钮视图
@property(nonatomic) BOOL showsScopeBar;

// 搜索框背景图片
@property(nullable, nonatomic,strong) UIImage *backgroundImage;
// 搜索框附属分栏条的背景颜色
@property(nullable, nonatomic,strong) UIImage *scopeBarBackgroundImage;

// 搜索框中文本框的背景偏移量
@property(nonatomic) UIOffset searchFieldBackgroundPositionAdjustment;
// 搜索框中文本框的文本偏移量
@property(nonatomic) UIOffset searchTextPositionAdjustment;

2、UISearchBar的方法

// 设置是否动画效果的显示或隐藏取消按钮
 - (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated 

// 1.设置搜索框背景图片
- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics 
//  获取搜索框背景图片
- (nullable UIImage *)backgroundImageForBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics 

// 2.设置选择按钮视图的背景图片
- (void)setScopeBarButtonBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state 
// 获取选择按钮视图的背景图片
- (nullable UIImage *)scopeBarButtonBackgroundImageForState:(UIControlState)state  

// 3.设置搜索框文本框的背景图片
- (void)setSearchFieldBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state 
// 获取搜索框文本框的背景图片
- (nullable UIImage *)searchFieldBackgroundImageForState:(UIControlState)state

// 4.设置搜索框的图标
- (void)setImage:(nullable UIImage *)iconImage forSearchBarIcon:(UISearchBarIcon)icon state:(UIControlState)state;
// 获取搜索框的图标
- (nullable UIImage *)imageForSearchBarIcon:(UISearchBarIcon)icon state:(UIControlState)state;

// 5.设置选择按钮视图的分割线图片
- (void)setScopeBarButtonDividerImage:(nullable UIImage *)dividerImage forLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState;
// 获取选择按钮视图的分割线图片
- (nullable UIImage *)scopeBarButtonDividerImageForLeftSegmentState:(UIControlState)leftState rightSegmentState:(UIControlState)rightState;

// 6.设置选择按钮视图的标题样式
- (void)setScopeBarButtonTitleTextAttributes:(nullable NSDictionary<NSString *, id> *)attributes forState:(UIControlState)state;
// 获取选择按钮视图的标题样式
- (nullable NSDictionary<NSString *, id> *)scopeBarButtonTitleTextAttributesForState:(UIControlState)state

//  设置搜索框图标的偏移量
- (void)setPositionAdjustment:(UIOffset)adjustment forSearchBarIcon:(UISearchBarIcon)icon;
//  获取搜索框图标的偏移量
- (UIOffset)positionAdjustmentForSearchBarIcon:(UISearchBarIcon)icon;

3、UISearchBarDelegate代理方法

// 将要开始编辑文本时会调用该方法,返回 NO 将不会变成第一响应者
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar;                      
// 开始输入文本会调用该方法
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar;                     
// 将要结束编辑文本时会调用该方法,返回 NO 将不会释放第一响应者
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar; 
 // 结束编辑文本时调用该方法
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar;     
// 文本改变会调用该方法(包含clear文本)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText; 
// 文字改变前会调用该方法,返回NO则不能加入新的编辑文字
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text ;

// 键盘上的搜索按钮点击的会调用该方法
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar;     
// 搜索框右侧图书按钮点击会调用该方法
- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar ;
 // 点击取消按钮会调用该方法
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar;   
// 搜索结果列表按钮被按下会调用该方法
- (void)searchBarResultsListButtonClicked:(UISearchBar *)searchBar ; 
// 搜索框的附属按钮视图中切换按钮会调用该方法
- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope;

九、UINavigationController

导航栏

1、配置返回按钮

- (void)createBackBarButton
{
    // 自定义后退按钮的文字和颜色
    // 如果不想显示文字,直接"",就会单独显示一个系统的返回箭头图标
    // 必须放在触发push的那个页面才起作用
    // 设置返回按钮的属性
    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] init];
    NSMutableDictionary *backTextAttrs = [NSMutableDictionary dictionary];
    backTextAttrs[NSFontAttributeName] = [UIFont fontWithName:@"GillSans-SemiBoldItalic" size:20];// 字号
    [backItem setTitleTextAttributes:backTextAttrs forState:UIControlStateHighlighted];// 高亮状态
    backItem.title = @"返回";
    self.navigationItem.backBarButtonItem = backItem;
}

2、配置导航栏

- (void)createNavigationController
{
    self.navigationItem.title = @"详情界面";
    
    // 使用图片作为导航栏标题
    self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"luckcoffee.JPG"]];
    
    // 导航栏右边按钮添加文本
    UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"编辑" style:UIBarButtonItemStylePlain target:nil action:nil];
    self.navigationItem.rightBarButtonItem = item;
    self.navigationItem.rightBarButtonItems = @[item,item];// 数组
    
    // 导航栏右边按钮添加图片后仍然使按钮保持原来的系统颜色
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:@"luckcoffee.JPG"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] style:UIBarButtonItemStylePlain target:nil action:nil];
    
    // 设置导航栏标题的属性
    NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
    textAttrs[NSForegroundColorAttributeName] = [UIColor redColor];// 字体颜色
    textAttrs[NSFontAttributeName] = [UIFont fontWithName:@"GillSans-SemiBoldItalic" size:18];// 字号
    self.navigationController.navigationBar.titleTextAttributes = textAttrs;
    
    // 设置导航栏的背景颜色
    [self.navigationController.navigationBar setBarTintColor:[UIColor greenColor]];
    [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}];
    
    // 给导航栏添加背景图片
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"luckcoffee.JPG"] forBarMetrics:UIBarMetricsDefault];
    
    // 获取选中的TabBar
    UINavigationController *navigationController =  (UINavigationController *)self.parentViewController;
    NSString *selectTab = navigationController.tabBarItem.title;
    NSLog(@"选中的TabBar标题为:%@",selectTab);
}

3、UITabBarController

// push到下个界面隐藏底部TabBar
loginViewController.hidesBottomBarWhenPushed = YES;

// 获取当前选中的item
selectedItem

// badge的数值
tabBarItem.badgeValue = @"10";

// Tab标签栏跳转,比如从用户中心跳转到首页:
[self.navigationController popViewControllerAnimated:NO];
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
UITabBarController *tab = (UITabBarController *)delegate.window.rootViewController;
tab.selectedIndex = 0;

// 数组
self.viewControllers = @[redVC, blueVC];

十、GestureRecognizer

1、Tap: 点击

2020-10-29 15:43:11.537921+0800 UIKitGrammarDemo[27813:955310] 点击改变testView的颜色
Tap: 点击
// Tap: 点击
- (void)tapGestureRecognizer
{
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapView:)];
    tap.numberOfTapsRequired = 2;// 设置轻拍次数
    tap.numberOfTouchesRequired = 2;// 设置手指字数

    [self.gestureRecognizerView addGestureRecognizer:tap];
}

- (void)tapView:(UITapGestureRecognizer *)sender
{
    // 改变testView的颜色
    self.gestureRecognizerView.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1];
    NSLog(@"点击改变testView的颜色");
}

2、Swipe: 轻扫

2020-10-29 15:49:03.819006+0800 UIKitGrammarDemo[1244:183307] 轻扫改变testView的颜色
2020-10-29 15:49:09.874471+0800 UIKitGrammarDemo[1244:183307] 从左往右轻扫
- (void)swipeGestureRecognizer
{
    UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeView:)];
    swipe.numberOfTouchesRequired = 1;// 设置手指个数
    swipe.direction = UISwipeGestureRecognizerDirectionLeft;// 设置轻扫方向(默认是从左往右)

    [self.gestureRecognizerView addGestureRecognizer:swipe];
}

- (void)swipeView:(UISwipeGestureRecognizer *)sender
{
    // 改变testView的颜色
    self.gestureRecognizerView.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1];
    NSLog(@"轻扫改变testView的颜色");
    
    if (sender.direction == UISwipeGestureRecognizerDirectionLeft)
    {
        NSLog(@"从左往右轻扫");
    }
}

3、LongPress: 长按

2020-10-29 15:51:28.415827+0800 UIKitGrammarDemo[1248:184810] 长按改变testView的颜色
- (void)longPressGestureRecognizer
{
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    longPress.minimumPressDuration = 2;// 最小长按时间
    
    [self.gestureRecognizerView addGestureRecognizer:longPress];
}

- (void)longPress:(UILongPressGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateBegan)// 在什么时候触发事件
    {
        // 改变testView的颜色
        self.gestureRecognizerView.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1];
        NSLog(@"长按改变testView的颜色");
    }
}

4、Pan: 平移

2020-10-29 16:35:39.932100+0800 UIKitGrammarDemo[28022:965824] 平移改变testView的颜色
Pan: 平移
- (void)panGestureRecognizer
{
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panView:)];
    [self.gestureRecognizerView addGestureRecognizer:pan];
}

- (void)panView:(UIPanGestureRecognizer *)sender
{
    // 起始点
    CGPoint point = [sender translationInView:self.gestureRecognizerView];
    
    // 第一种移动方法:每次移动都是从原来的位置移动
    sender.view.transform = CGAffineTransformMakeTranslation(point.x, point.y);
    
    // 第二种移动方式:以上次的位置为标准(第二次移动加上第一次移动量)
    sender.view.transform = CGAffineTransformTranslate(sender.view.transform, point.x, point.y);
    // 增量置为0
    [sender setTranslation:CGPointZero inView:sender.view];
    
    // 改变testView的颜色
    self.gestureRecognizerView.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1];
    
    NSLog(@"平移改变testView的颜色");
}

5、ScreenEdgePan: 屏幕边缘平移

- (void)screenEdgePanGestureRecognizer
{
    UIScreenEdgePanGestureRecognizer *screenEdgePan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(screenEdgePanView:)];
    screenEdgePan.edges = UIRectEdgeLeft;// 视图位置(屏幕边缘)
    [self.gestureRecognizerView addGestureRecognizer:screenEdgePan];
}

// 不走这个方法......
- (void)screenEdgePanView:(UIScreenEdgePanGestureRecognizer *)sender
{
    // 获取拖动的位置
    CGPoint point = [sender translationInView:sender.view];
    
    // 每次都以传入的translation为起始参照
    sender.view.transform = CGAffineTransformTranslate(sender.view.transform, point.x, 0);
    
    // 设置当前拖动的位置
    [sender setTranslation:CGPointZero inView:sender.view];
    
    // 改变testView的颜色
    self.gestureRecognizerView.backgroundColor = [UIColor colorWithRed:arc4random()%256/255.0 green:arc4random()%256/255.0 blue:arc4random()%256/255.0 alpha:1];
    
    if (sender.edges == UIRectEdgeLeft)
    {
        NSLog(@"从左边缘向右平移");
    }
}

获取屏幕边缘扇动手势

- (UIScreenEdgePanGestureRecognizer *)screenEdgePanGestureRecognizer
{
    UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = nil;
    
    if (self.view.gestureRecognizers.count > 0)
    {
        for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
        {
            NSLog(@"手势:%@",recognizer);
            if ([recognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
            {
                screenEdgePanGestureRecognizer = (UIScreenEdgePanGestureRecognizer *)recognizer;
                break;
            }
        }
    }
    
    return screenEdgePanGestureRecognizer;
}

输出结果为:

2020-09-25 14:51:28.050270+0800 FunctionCodeBlockDemo[9250:18335987] 手势:<UIScreenEdgePanGestureRecognizer: 0x7fb58ce046d0; state = Possible; delaysTouchesBegan = YES; view = <UIView 0x7fb58f8068f0>; target= <(action=compare:, target=<LittleMethodViewController 0x7fb58cd06e70>)>>

6、Pinch: 捏合

Pinch: 捏合
- (void)pinchGestureRecognizer
{
    // 设置缩放比例
    self.lastScale = 1;
    self.minScale = 0.5;
    self.maxScale = 2;
    
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchView:)];
    [self.gestureRecognizerView addGestureRecognizer:pinch];
}

- (void)pinchView:(UIPinchGestureRecognizer *)recognizer
{
    switch (recognizer.state)
    {
        case UIGestureRecognizerStateBegan:// 缩放开始
        case UIGestureRecognizerStateChanged:// 缩放改变
        {
            CGFloat currentScale = [[self.gestureRecognizerView.layer valueForKeyPath:@"transform.scale"] floatValue];
            CGFloat newScale = recognizer.scale - self.lastScale + 1;
            newScale = MIN(newScale, self.maxScale / currentScale);
            newScale = MAX(newScale, self.minScale / currentScale);
            
            self.gestureRecognizerView.transform = CGAffineTransformScale(self.gestureRecognizerView.transform, newScale, newScale);
            self.lastScale = recognizer.scale;
            
            break;
        }
        case UIGestureRecognizerStateEnded:// 缩放结束
            self.lastScale = 1;
            break;
        default:
            break;
    }
}

7、Rotation: 旋转

Rotation: 旋转
- (void)rotationGestureRecognizer
{
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationView:)];
    [self.gestureRecognizerView addGestureRecognizer:rotation];
}

- (void)rotationView:(UIRotationGestureRecognizer *)sender
{
    CGFloat rotationAngleInRadians = 0;// 旋转角度
    // 手势识别完成,保存旋转的角度
    if (sender.state == UIGestureRecognizerStateEnded)
    {
        rotationAngleInRadians += sender.rotation;
    }
    // 将上一次角度加上本次旋转的角度作为本次旋转的角度
    self.view.transform = CGAffineTransformMakeRotation(rotationAngleInRadians + sender.rotation);
}

8、支持多个UIGestureRecongnizer共存

// 先接受到了手势事件,直接就处理而没有往下传递实际上就是两个手势共存的问题,先执行了UIScrollerView中包含的手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    // 滑动返回无法触发,说明UIScreenEdgePanGestureRecongnizer并没有接受到手势事件
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]  && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
    {
        // 返回YES,手势事件会一直往下传递,不论当前层次是否对该事件进行响应
        return YES;
    }
    else
    {
        return NO;
    }
}

9、响应屏幕触摸

// 手指按下时响应
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    NSLog(@"手指按下时响应");
}

// 手指移动时响应
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];
    NSLog(@"手指移动时响应");
}

// 手指抬起时响应
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    NSLog(@"手指抬起时响应");
}

// 触摸取消(意外中断, 如:电话, Home键退出等)
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"取消触摸响应(意外中断, 如:电话, Home键退出等)");
}

输出结果为:

2020-10-30 13:50:47.723917+0800 UIKitGrammarDemo[3009:597013] 手指按下时响应
2020-10-30 13:50:47.746602+0800 UIKitGrammarDemo[3009:597013] 手指移动时响应
2020-10-30 13:50:16.141409+0800 UIKitGrammarDemo[3009:597013] 手指抬起时响应
2020-10-30 13:50:47.747051+0800 UIKitGrammarDemo[3009:597013] 取消触摸响应(意外中断, 如:电话, Home键退出等)

10、运动事件监听

// 开始加速
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    [super motionBegan:motion withEvent:event];
    NSLog(@"开始加速");
}

// 结束加速
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    [super motionEnded:motion withEvent:event];
    NSLog(@"结束加速");
}

// 加速取消(意外中断, 如:电话, Home键退出等)
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    [super motionCancelled:motion withEvent:event];
    NSLog(@"加速取消");
}

输出结果为:

2020-10-30 13:51:02.554064+0800 UIKitGrammarDemo[3009:597013] 开始加速
2020-10-30 13:51:03.149090+0800 UIKitGrammarDemo[3009:597013] 结束加速

11、手势谁先响应

a、Demo演示
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 100, 100)];
btn.backgroundColor = UIColor.redColor;
[btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[tap addTarget:self action:@selector(tap)];
[btn addGestureRecognizer:tap];

[self.view addSubview:btn];
- (void)click
{
    NSLog(@"控件的点击事件");
}

- (void)tap
{
    NSLog(@"手势");
}

输出结果为:

2020-07-21 17:59:15.797539+0800 Demo[72815:21154355] 手势

b、原因分析

当手指触碰到屏幕,无论是单点还是多点触碰,事件都会开始,直到用户所有的手指都离开屏幕。期间所有的UITouch对象都被封装在UIEvent事件对象中。因为UIEvent包含了整个触碰过程中所有的触碰对象,所以可以调用allTouches 方法获取该事件内所有触碰对象。也可以调用touchesForView 或者 touchesForWindows 取出特定视图或者窗口上的触碰对象。

因为单击手势优先于UIView的事件响应,如果手势识别成功,就会直接取消事件的响应链传递。如果手势识别失败了,触摸事件会继续走传递链,传递给响应链处理。但是手势识别是需要时间的,在possible状态的时候,单击事件也可能已经传递给响应链了,如果手势识别器识别出触摸手势,会将传递给响应链的事件取消掉。

手势在触碰事件处理流程中,处于观察者的角色,其不是view层级结构的一部分,所以不参与响应者链。在将触摸事件发送给hit-test view之前,系统会先将触碰事件发送到view绑定的Gesture Recognizer上。


c、触碰冲突相关的属性
cancelsTouchesInView

YES时,表示当Gesture Recognizers识别到手势后,会给hit-test view发送touchesCancelled:消息以取消hit-test view对触碰序列的处理,这样只要Gesture Recognizer响应此次触碰,响应者链的view不再响应。如果为NO,则不发送touchesCancelled:消息,这样Gesture Recognizerview同时响应触碰事件。默认值是YES

delaysTouchesBegan

NO时表示触碰序列已经开始而手势识别还未识别出此手势时,touch事件会同时发给hit-test view。为YES时,延迟发送touchesEnded:消息,手势失败时才发送。默认值是YES

userInteractionEnabled

默认是YES,如果设置为NO则不响应用户事件,并且把当前控件从事件队列中删除。也就是说设置了userInterfaceEnabled属性为NO的视图会打断响应者链导致该viewsubview都无法响应事件。

multipleTouchEnabled

默认是NO,如果设置为YES则支持多点触碰。

exclusiveTouch

默认是NO,如果设置为YES则当前UIView会独占整个Touch事件。具体来说就是如果UIView设置了exclusiveTouch属性为YES则当这个UIView成为第一响应者时,在手指离开屏幕前其他view不会响应任何touch事件。比如UITableView的每个cell都需要使用exclusive,否则同时点击多个cell会触发每个视图的事件响应。手势识别会忽略此属性。


12、手势提供的属性和方法

手势状态
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
可以通过这个属性获取当前UIView的所有手势对象
NSArray *gestureRecognizers
添加手势
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer//增加一个手势
- (void)removeGestureRecognizer:(UIGestureRecognizer *)getureRecognizer//删除一个手势
- (BOOL)gestureRecognizerShouldBegan:(UIGestureRecognizer *)gestureRecognizer//询问是否开始执行该手势,默认返回YES
手势种类
UITabGestureRecognizer// 轻击手势
numberOfTapsRequired// 点击次数
numberOfTouchesRequired// 手指个数
UIPinchGestureRecognizer// 捏合手势
scale// 初始值为1,两手指距离减少则scale不断变小;两个手指重合则变为0
velocity// 初始值为0,手指移动的相对速度,两手指距离减少为负数,速度越快数值越少;两手指距离变大为整数,速度越快数值越大
UIRotationGestureRecognizer// 旋转手势
rotation// 初始值为0,两手指的旋转弧度,顺时针旋转为正数,逆时针旋转为负数
velocity// 初始值为0手指一动的相对速度,顺时针为正数越快值越大;逆时针为负越快越小
UISwipeGestureRecognizer// 轻扫手势
numberOfTouchesRequired// 手指个数
direction// 手势方向,如UISwipeGestureRecognizerDirectionRight向右
UIPanGestureRecognizer// 拖拽手势
mininumNumberOfTouches// 默认值为1,最少手指数量
maxnumNumberOfTouches// 最大手指数量
UILongPressGestrueRecognizer// 长按手势
numberOfTapsRequired// 默认值为0,轻击的次数
numberOfTouchesRequired// 默认值是1,手指数量
mininumPressDuration// 默认值为0.5,单位是秒
allowableMovement// 默认值为10,单位是像素pixels
调用的方法
- (id) initWithTarget:action:// 初始化方法
- (void)addTarget:action:
- (void)removeTarget:action:
手势识别当前所处状态
UIGestureRecognizerStatePossibel// 未识别状态
UIGestureRecognizerStateBegan// 手势开始
UIGestureRecognizerStateChanged// 手势改变
UIGestureRecognizerStateEnded// 手势结束
UIGestureRecognizerStateFailured // 手势失败,被其他事件中断。当把手势state设为这个值得时候相当于取消了这个手势
多手势兼容问题
[panRecognizer requireGestureRecognizerToFail:swipeRecognizer];// 捏合手势失败后才会触发拖拽手势。如果捏合手势成功则拖拽手势永远不会被触发
[rotationGestureRecognizer canBePreventedByGestureRecognizer:pinchGestureRecognizer];// 如果rotation手势重载了canBePreventedByGestureRecognizer方法并且返回YES。则旋转手势被捏合手势阻止
[rotationGestureRecognizer canPreventGestureRecognizer:pinchGestureRecognizer];// 如果rotation手势重载了canBePreventedByGestureRecognizer方法并且返回YES。则旋转手势阻止了捏合手势
UIGestureRecognizerDelegate
// 此方法在gesture recognizer视图传出UIGestureRecognizerStatePossible状态时调用,如果返回NO,则转换成UIGestureRecognizerStateFailed;如果返回YES,则继续识别。默认返回YES
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

// 此方法在window对象有触碰事件发生时,touchesBegan:withEvent:方法之前调用。如果返回NO,则GestureRecognizer忽略此触碰事件。默认返回YES。可以用于禁止某个区域的手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

// 如果有多个手势接收到了同一个消息,该回调方法决定当前手势是否要响应该事件,如果返回YES则该事件被响应,如果返回NO该事件将被忽略
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

Demo

Demo在我的Github上,欢迎下载。
BasicsDemo

参考文献

iOS--UISearchBar 属性、方法详解及应用(自定义搜索框样式)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容