iOS常见控件的基本使用

时间:2021-10-20 06:29:57

UI相关类继承关系

iOS常见控件的基本使用

UIView 常见属性和方法

1. UIView属性

Controller的viewDidLoad方法:当所有控件加载完毕后调用,相当于Android中View的onFinishInflate方法;

UIView superview 获得自己的父控件对象;

NSArray subview 获得自己的子控件对象,用数组NSArray保存;

NSInteger tag 控件的ID标示,父控件可以通过tag来找到对应的子控件;

CGRect fram 控件所在矩形框的位置和尺寸(以父控件的左上角为坐标原点);

CGRect bounds 控件所在矩形框的位置和尺寸(以自己左上角为坐标原点,所以bounds的x\y永远为0);

CGPoint center 控件中点的位置(以父控件 的左上角为坐标原点);

CGAffineTransform transform 控件的形变属性(可以设置旋转角度、比例缩放、平移等属性);

UIColor backgroundColor 背景颜色

BOOL hidden 设置是否隐藏

CGFloat alpha 透明度(0~1)

CGFloat opaque 不透明度(0~1)

BOOL userInteractionEnabled 默认YES,是否可以和用户交互(相当于android enable)

UIViewContentMode contentMode 内容显示的模式(android:gravity =”center_vertical”)

另外UIView中有很多扩展协议,用来处理控件的属性

修改UIView的尺寸(宽高):frame、bounds

修改UIView的位置:frame(左上角的位置)、center(中点的位置)

2. UIView方法

addSubview 添加子控件,被添加到最上面(subviews中的最后面)

removeFromSuperview 将自己从父控件中移除

viewWithTag 父控件可以根据这个tag标示找到对应的子控件(遍历所有的子控件)findViewById

insertSubview:atIndext 添加子控件到指定的位置

beginAnimations: context: 执行动画

/…需要执行动画的代码…/

commitAnimations

利用代码块block执行动画

/*
 *duration 动画持续时间
 *animations 存放需要执行动画的代码
 *completion 存放动画执行完毕后需要执行的代码
 */
 + (void)animateWithDuration:(NSTimeInterval) duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

3. UIControl

只要继承了UIControl就能简单处理一些事件(点击事件、值改变事件);

继承了UIControl的子类有:UIButton、UISlider、UISwitch、UIDatePicker等等;

当需要监听一个子控件的事件的时候,先要看它是否继承自UIControl,再看它内部是否有delegate属性(代理);

常用属性:

enable 是否处理事

contentVerticalAlignment 内容在垂直方向上的排布方式

contentHorizontalAlignment 内容在水平方向上的排布方式

常用方法

addTarget 添加事件监听器(参数:监听器对象、事件触发回调方法、事件类型)

removeTarget 删除事件监听器

allTargets 获取所有的监听器对象集合


常用控件

1. UIImageView 图片显示控件(android ImageView)

@property (weak, nonatomic) IBOutlet UIImageView *imageView;
self.imageView.image = [UIImage imageNamed:@“icon.png"];

设置图片展示模式(android ImageView 的scaleType属性):

iOS常见控件的基本使用

2. UISlider可拖动的进度条 (android SeekBar

@property (weak, nonatomic) IBOutlet UISlider *slider;
//设置最大值
self.slider.maximumValue = self.imageDate.count;
//设置最小值
self.slider.minimumValue = 1;
//设置当前值
self.slider.value = 1;

3. UIButton按钮

常用属性

titleLable 获取内部的UILabel对象

imageView 获取内部的UIImageView对象

常见方法

setTitle:forState: 设置背部UILable显示的文本内容(不能写btn.titleLabel.text = @"123"

setTileColor:forState 设置内部UILabel文字颜色

setTileShaowColor:forState 设置内部UILabel文字的阴影颜色

setImage:forState 设置内部UIImageView的图片(不能写btn.iamgeView.image =[ UIImage imagedName:@"0.png"])

setTileShaowColor:forState 设置内部UILabel文字的阴影颜色

setBackgroundImage:forState 设置背景图片

下面方法可以获取不同状态下的一些属性值:

titleForState 获取某个状态下显示文字

titleColorForState 获取某个状态下文字颜色

titleShadowColorForState 获取某个状态下文字阴影颜色

imageForState 获取某个状态下图片

backgroundImageForState 获取某个状态下背景图片

下面两个方法需要交给子类重写(继承UIButton):

titleRectForContentRect 返回内部UILabel的frame(位置和尺寸)

imageRectForContentRect 返回内部UIImageView的frame(位置和尺寸)

示例代码

普通按钮custom

//创建按钮
UIButton*orangeBtn = [[UIButtonalloc]init];
orangeBtn.tag=kOrangeTag;
//设置按钮的frame(位置和尺寸)
orangeBtn.frame=CGRectMake(100,100,100,100);
//设置背景色
orangeBtn.backgroundColor= [UIColororangeColor];
//设置按钮文字
[orangeBtnsetTitle:@"普通"forState:UIControlStateNormal];
[orangeBtnsetTitle:@"按下"forState:UIControlStateHighlighted];
//设置按钮按下时文字颜色
[orangeBtnsetTitleColor:[UIColorredColor]forState:UIControlStateHighlighted];
UIButton*btn = [[UIButtonalloc]init];
//添加按钮的唯一标示
btn.tag = 1;
//根据图片名称去项文件系统中加载图片对象
UIImage*imageNormal = [UIImageimageNamed:@"sub_black_prev.png"]];
UIImage*imagePress = [UIImageimageNamed:@"sub_blue_prev.png"]];
//设置按钮背景图片
[btnsetBackgroundImage:imageNormalforState:UIControlStateNormal];
[btnsetBackgroundImage:imagePressforState:UIControlStateHighlighted];
//设置按钮的frame(位置和尺寸)//btn.frame = CGRectMake(point.x, point.y, imageNormal.size.width, imageNormal.size.height);
   /*
     struct CGRect {
     CGPoint origin;
     CGSize size;
     };
    */
btn.frame = (CGRect){point, imageNormal.size};
//绑定监听
[btn addTarget:self action:@selector(directionBtnClick:) forControlEvents:UIControlEventTouchUpInside];
//根据按钮的tag获取子控件
UIButton*orangeBtn = [self.viewviewWithTag:kOrangeTag];
CGPoint center = orangeBtn.center;
staticCGFloatDELETE =50;
center.y += DELETE;
//重新赋值按钮的中点
orangeBtn.center= center;

根据系统自带样式类型创建

//创建一个加号的按钮
UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
btn.center = CGPointMake(100, 100);

4. UILable 文本 (android TextView)

常用属性:

text 显示文本内容

textColor 文字颜色

font 字体

shadowColor 文字的阴影颜色

shadowOffset 阴影的偏差距离(width水平方向的偏差距离,整数右边;height垂直方向上的偏差距离,正数下边)

textAlignment 设置文字的排列方式(偏左、偏右、居中)

numberOfLines 允许文字最多几行(默认1,如果是0,自动换行)

5. UIText 文本输入框(android EditText)

6. UISwitch 开关

#pragma mark 开关值改变监听
-(IBAction)switchChanged:(UISwitch *)sender {
    NSLog(@"开关值%d", sender.isOn);
    self.view.backgroundColor = sender.isOn?[UIColor darkGrayColor]:[UIColor whiteColor];
}

7. UIDatePicker日期控件

-(void) viewDidLoad{
    [super viewDidLoad];

    UIDatePicker *picker = [[UIDatePicker alloc] init];
    //设置区域
    picker.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
    //设置模式(显示日期还是时间)
    picker.datePickerMode = UIDatePickerModeDate;

}
#pragma mark 拖动时间改变后监听
-(IBAction)dateChanged:(UIDatePicker *) dataPicker{
    //获取日期值
    NSDate * date = dataPicker.date;
    //格式化日期
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy/MM/bb";

    self.label.text = [formatter stringFromDate:date];
}

8. UIPickerView 跟android Spinner相似

设置数据源和代理(监听)为控制器,控制器遵循数据源河代理的协议,重写协议的方法:

  • 简单使用
#pragma mark 控制器充当UIPickerView的数据源和代理,必须遵循两个协议
@interface ViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
@property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
@end

#import "ViewController.h"
@interface ViewController()
@property (nonatomic,strong) NSArray *oneCol;
@property (nonatomic,strong) NSArray *towCol;
@end

@implementation ViewController
-(void) viewDidLoad{
    self.oneCol = @[@"00", @"01", @"02", @"03"];
    self.towCol = @[@"000", @"001", @"002", @"003", @"004"];
}
#pragma mark - UIPickerView的数据源方法(返回第component列的行数)
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{

    return component == 0?self.oneCol.count:self.towCol.count;
}
#pragma mark - UIPickerView的数据源方法(返回列数)
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 2;
}
#pragma mark - UIPickerView的代理方法(返回第component列第row航显示的字符串数据-设置数据)
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    if(component==0){
        return self.oneCol[row];
    }else{
        return self.towCol[row];
    }
}
#pragma mark - UIPickerView的代理方法(选中了某一行调用)
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
    if(component==0){
        NSString *leftRow = self.oneCol[row];
        //获取第二列当前停留的行数
        int towRow = [pickerView selectedRowInComponent:1];
        NSString *rightRow = self.towCol[towRow];
        NSLog(@"第%d列:%@-----第%d列:%@", component, leftRow, 1, rightRow);
    }else{
        NSString *rightRow = self.towCol[row];
        int oneRow = [pickerView selectedRowInComponent:0];
        NSString *leftRow = self.oneCol[oneRow];
        NSLog(@"第%d列:%@-----第%d列:%@", 0, leftRow, 1, rightRow);
    }
}
@end
  • 显示自定义条目
#pragma mark 修改每一行的高度
-(CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{
    return 70;
}
#pragma mark 返回地component列第row行需要显示的控件
-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{
    UIView *rowView = [[UIView alloc] init];
    CGFloat rowViewH = 40;
    CGFloat rowViewW = 200;
    rowView.bounds = CGRectMake(0, 0, 200, rowViewH);

    //国家名称
    UILabel *nameLable = [[UILabel alloc] init];
    CGFloat nameW = 70;
    nameLable.frame = CGRectMake(0, 0, nameW, rowViewH);
    nameLable.textAlignment = NSTextAlignmentCenter;
    nameLable.text = @"asdfsd";
    [rowView addSubview:nameLable];
    //国旗
    UIImageView *imageView = [[UIImageView alloc] init];
    imageView.frame = CGRectMake(nameW, 0, rowViewW-nameW, rowViewH);
    imageView.image = [UIImage imageNamed:@"zhongguo.jpg"];
    [rowView addSubview:imageView];

    return rowView;
}

9. UIScollView (android ScollView)

UIPageControl 请见:分页显示

当子控件内容太多或者子控件太大显示不全时,用UIScrollView实现滚动

  • 常用属性

    CGPoint contentOffset UIScrollView当前滚动到哪个位置了(相对于内容左上角的坐标)

    CGSize contentSize UIScrollView的滚动范围(内容的尺寸)

    UIEdgeInsets contentInset 这个属性可以在四周增加滚动范围

    iOS常见控件的基本使用

    BOOL bounces; 是否有弹簧效果

    BOOL scrollEnabled; 是否能滚动

    BOOL showsHorizontalScrollIndicator 是否显示水平方向的滚动条

    BOOL showsVerticalScrollIndicator 是否显示垂直方向的滚动条

    UIScrollViewIndicatorStyle indicatorStyle 设定滚动条的样式

    BOOL tracking 是否正在被拖拽

    BOOL dragging 当touch后还没有拖动的时候值是YES,否则是NO

    BOOL decelerating 是否正在减速

  • 使用示例:

UIImage *image = [UIImage imageNamed:@"1.png"];
//通过图片初始化UIImageview,这样ImageView的宽高就跟图片宽高一样了
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.scroll addSubview:imageView];
//self.scroll.frame.size  UIScrollView控件的大小(可视范围)
//设置内容的宽高(可滚动范围)
self.scroll.contentSize = image.size;
//为UIScrollView设置上下左右额外的可拖动范围(空隙)
self.scroll.contentInset = UIEdgeInsetsMake(10, 20, 30, 40);
self.scroll.backgroundColor = [UIColor lightGrayColor];
   //self.scroll.contentOffset.x += 10;//不能直接修改对象的结构体属性的成员
//修改UIScrollView当前拖动的位置(相对于内容左上角的坐标)
CGPoint offset = self.scroll.contentOffset;
//向左移动(查看右边的内容)
offset.x += 50;
//执行动画
[UIView animateWithDuration:0.3 animations:^{
    self.scroll.contentOffset = offset;
}];
  • 手势识别缩放
@property(nullable,nonatomic,weak) id<UIScrollViewDelegate>        delegate;
iOS中的UIScrollView已经封装了手势识别,不需要我们分析手指位置以及滑动方向,只需要设置上面的代理属性即可(代理可以理解为回调接口),这个代理需要遵守UIScrollViewDelegate协议;然后选择性的实现里面的回调方法。
而android中如果要实现此功能,需要我们分析多点触控以及手指位置和滑动方向来判断缩放还是放大。</br>

步骤:

①、设置UIScrollView的代理delegate为控制器,控制器遵守UIScrollViewDelegate协议

②、设置最大和最小缩放比例maximumZoomScaleminimumZoomScale

③、让代理对象实现方法,返回需要缩放的子控件

//设置最大缩放比例
self.scroll.maximumZoomScale = 2;
//设置最小缩放比例
self.scroll.minimumZoomScale = 0.5;
 #pragma mark -UIScrollView的代理方法:返回需要进行缩放的空间(必须是UIScrollVIew的子控件)
(nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
    return self.imageView;
}
  • 代理中其他方法:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;    // any offset changes offset属性变化时调用(在被拖动)
// 将要开始拖拽时调用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
// 拖拽将要结束时调用
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset NS_AVAILABLE_IOS(5_0);
// 拖拽结束时调用
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;   // called on finger up as we are moving
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;      // ScrollView减速完毕后调用
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView; // called when setContentOffset/scrollRectVisible:animated: finishes. not called if not animating

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;     // 返回需要进行缩放的子控件
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view NS_AVAILABLE_IOS(3_2); // 将要缩放之前调用
- (void)scrollViewDidZoom:(UIScrollView *)scrollView NS_AVAILABLE_IOS(3_2); // any zoom scale changes 缩放时调用
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale; // 缩放完毕后调用
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;   // return a yes if you want to scroll to the top. if not defined, assumes YES
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView;      // called when scrolling animation finished. may be called immediately if already at top
  • 分页显示(android ViewPager)

    iOS常见控件的基本使用
#define kCount 5
@interface ViewController ()
@property (nonatomic, weak) UIPageControl *_pageControl;
@property (nonatomic, weak) UIScrollView *_scrollView;
@end

@implementation ViewController
- (void)viewDidLoad {
   [super viewDidLoad];
   for(int i = 0; i<self.view.subviews.count; i++){
        [self.view.subviews[i] removeFromSuperview];
    }
    //分页显示
    //1、添加UIScrollView
    UIScrollView *scrollView = [[UIScrollView alloc] init];

    //设置填充父窗体
    scrollView.frame = self.view.bounds;
    [self.view addSubview:scrollView];
    self._scrollView = scrollView;

    CGFloat scrollWdith = scrollView.frame.size.width;
    CGFloat scrollHight = scrollView.frame.size.height;

    // 2、添加所有的ImageView
    for(int i = 0;i<=kCount;i++){
        //加载图片
        NSString *imageName = [NSString stringWithFormat:@"pages.bundle/%d.jpg", i];
        UIImage *image = [UIImage imageNamed:imageName];
        UIImageView *imageView = [[UIImageView alloc] init];
        imageView.image = image;
        imageView.frame = CGRectMake((i-1)*scrollWdith, 0, scrollWdith, scrollHight);
        [scrollView addSubview:imageView];

    }

    //3、设置滚动范围
    scrollView.contentSize = CGSizeMake(kCount*scrollWdith, 0);
    //隐藏滚动条
    scrollView.showsHorizontalScrollIndicator = NO;
    //4、开启分页功能(按照ScrollView的宽度将内容分为若干页,正好ImageView的宽度相等,就实现了分页)
    scrollView.pagingEnabled = YES;

    //5、添加PageControl
    UIPageControl *pageControl = [[UIPageControl alloc] init];
    pageControl.bounds = CGRectMake(0, 0, 150, 50);
    pageControl.center = CGPointMake(scrollWdith * 0.5, scrollHight - 50);
    //设置页数
    pageControl.numberOfPages = kCount;
    //当前选中页码对应控制器的颜色
    pageControl.currentPageIndicatorTintColor = [UIColor redColor];
    //其他控制器的颜色
    pageControl.pageIndicatorTintColor = [UIColor blackColor];
    //监听pageControl事件
    [pageControl addTarget:self action:@selector(pageChanged:) forControlEvents:UIControlEventValueChanged];

    //添加控制器
    [self.view addSubview: pageControl];

    self._pageControl = pageControl;

    //设置代理
    scrollView.delegate = self;
    //不需要弹簧效果
    scrollView.bounces = NO;

}
#pragma mark -滚动代理:减速完毕后调用(停止了)
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    CGFloat offset = scrollView.contentOffset.x;   //当前x轴方向上滚动了多少
    int pageNum = offset/scrollView.frame.size.width;
    //设置页码
    self._pageControl.currentPage = pageNum;
}
#pragma mark pageControl值变化时调用
-(void) pageChanged{
    CGFloat offsetX = self._pageControl.currentPage * self.view.frame.size.width;
    [UIView beginAnimations:nil context:nil];
    self._scrollView.contentOffset = CGPointMake(offsetX, 0);
    [UIView commitAnimations];
}
@end

10. UITableView( android ListView GridView ExpandListView)

10.1 数据源方法

TableView相当于ListView,设置数据源需要遵循UITableViewDataSource协议,相当于BaseAdapter,下面是协议的所有接口:

@protocol UITableViewDataSource<NSObject>
@required   必须实现的
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;   // 第section组有多少行数据
// 返回每一行显示的具体数据View(相当于getView()方法)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@optional  选择实现的
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;  // 一共有多少组数据(如果没有实现,默认为1)
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; // 分组头部标题
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; // 分组尾部标题
// Individual rows can opt out of having the -editing property set for them. If not implemented, all rows are assumed to be editable.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
// Moving/reordering
// Allows the reorder accessory view to optionally be shown for a particular row. By default, the reorder control will be shown only if the datasource implements -tableView:moveRowAtIndexPath:toIndexPath:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
//索引(e.g. "ABCD...Z#")
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView __TVOS_PROHIBITED;
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index __TVOS_PROHIBITED;  // tell table which section corresponds to section title/index (e.g. "B",1))
// Data manipulation - insert and delete support
// After a row has the minus or plus button invoked (based on the UITableViewCellEditingStyle for the cell), the dataSource must commit the change
// Not called for edit actions using UITableViewRowAction - the action's handler will be invoked instead
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;
// Data manipulation - reorder / moving support
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
@end

10.2 代理 UITableViewDelegate

// 返回条目高度(可以根据不同的行号返回不同的高度)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
//  选中某一行的时候调用(点击)
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
// 取消选中某一行
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

10.3 常用属性和方法

iOS常见控件的基本使用

10.4 多组数据

iOS常见控件的基本使用

@interface ViewController ()
     @property (nonatomic, strong) NSArray *gd;
     @property (nonatomic, strong) NSArray *hn;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.gd = @[@"广州", @"东莞", @"惠州"];
    self.hn = @[@"常德", @"长沙", @"湘潭", @"株洲", @"岳阳", @"湘西自治州"];
}
#pragma mark - 数据源方法
#pragma mark - 1、一共多少组数据
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 2;   //广东、湖南
}
#pragma mark - 2、第section组有多少行数据
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
   return section==0?self.gd.count:self.hn.count;
}
#pragma mark - 3、返回每一行显示的具体数据
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    // 组 indexPath.section
    // 列 indexPath.row
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
    NSString *city = nil;

    if(indexPath.section == 0){
        city = self.gd[indexPath.row];
    }else {
        city = self.hn[indexPath.row];
    }
    //设置右边箭头样式
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    //设置cell上面显示的文本数据
    cell.textLabel.text = city;
    return cell;
}
#pragma mark - 4、 第section组的header标题
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    return section == 0? @"广州" : @"湖南";
}
#pragma mark - 5、 第section组的尾部标题
-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
    return section == 0? @"广东很多帅哥": @"湖南很多美女";
}
#pragma mark- 6、 索引,通讯录效果,是按照分组而不是拼音首字母的索引
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView __TVOS_PROHIBITED{
    return @[@"广州", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南", @"湖南"];
}

#pragma mark- UITableView代理方法
#pragma mark- 1、 返回某一行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 70;
}
#pragma mark- 2、 选中某一行的时候调用(点击)
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString *city = indexPath.section == 0? self.gd[indexPath.row] : self.hn[indexPath.row];
    //弹出对话框修改地名
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"地名" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
    //设置alertview样式
    alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    //取出文本输入框
    [alert textFieldAtIndex:0].text = city;
    alert.tag = 1;
    //显示对话框
    [alert show];
}

#pragma mark- UIAlertView代理方法
#pragma mark- 对话框按钮点击
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex NS_DEPRECATED_IOS(2_0, 9_0){
    if(buttonIndex == 1){
        //点击了确定按钮
        //取得输入框文本
        NSString *city = [alertView textFieldAtIndex:0].text;
        NSString *old = self.gd[0];
        old = [NSString stringWithFormat:@"%@", city];
        //刷新UITableView数据,相当于android 的 adaptert.notifyDatesetChanged()
        //刷新所有数据
//        [self.tableView reloadData];
        //局部刷新
        NSIndexPath *path = [NSIndexPath indexPathForRow:alertView.tag inSection:0];
        [self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationLeft];
    }
}
@end

10.5 单组数据(ListView效果)

数据源返回分组数量的方法返回值为1就能实现单组效果

UITableViewCell系统样式:

iOS常见控件的基本使用

条目右边箭头样式:

iOS常见控件的基本使用

10.6 性能优化

  • 复用Cell

    Android的ListView在性能优化的时候复用convertView,这样getView方法不用每次加载创建条目View;iOS中UITableView的数据源方法cellForRowAtIndexPath中同样也可以复用缓存中的cell
      #pragma mark 每当有一个cell进入视野范围内就会调用,返回当前这行显示的cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 0.用static修饰的局部变量,只会初始化一次< # # >
    static NSString *ID = @"Cell";

    // 1.拿到一个标识先去缓存池中查找对应的Cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 2.如果缓存池中没有,才需要传入一个标识创建新的Cell
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
    }

    // 3.覆盖数据
    cell.textLabel.text = [NSString stringWithFormat:@" fdsfdsf-%d", indexPath.row];

    return cell;
}
  • 用户代码提醒

    Xcode中有很多系统自带的代码块,只需要拖动到编辑器中,就能自动生成代码,这样可以避免写很多重复代码,方便编程;也可以自定义用户代码块,比如上面复用Cell的模板方法,选中代码后拖动到右下角代码块中即可。

    iOS常见控件的基本使用

10.7 编辑模式

iOS常见控件的基本使用

  • 删除\添加

    开启编辑模式:
 // 开启编辑模式
    //self.tableView.editing = YES;
    //[self.tableView setEditing:YES];
    // 带有动画效果
    [self.tableView setEditing:! edt animated:YES];

下面方法的返回值决定编辑模式是添加还是删除:

#pragma mark - 代理方法
#pragma mark 当tableview开启编辑模式就会调用
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //return indexPath.row%2 ? UITableViewCellEditingStyleDelete : UITableViewCellEditingStyleInsert;
    return tableView.tag;
}

实现数据源的方法删除或添加数据:

#pragma mark 提交编辑操作(点击了"删除"或者"+"按钮)时调用
// 实现了这个方法,就有左划删除功能
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle  forRowAtIndexPath:(NSIndexPath *)indexPath
{
    //NSLog(@"------commit---%d", indexPath.row);

    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // 删除
        // 1.更改数据(删除本行的数据)
        [self.mydata removeObjectAtIndex:indexPath.row];

        // 2.刷新UI界面
        //[tableView reloadData];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
    } else {
        // 添加

        // 1.更改数据(添加数据)
        // [self.mydata addObject:@" hahahhahah"];添加到最后面去了
        // 插入数据到本行的后面
        [self.mydata insertObject:@"新添加的数据...." atIndex:indexPath.row + 1];

        // 2.刷新UI界面
        //[tableView reloadData];

        // 刷新指定行(个数不变)
        //[tableView reloadRowsAtIndexPaths:<#(NSArray *)#> withRowAnimation:<#(UITableViewRowAnimation)#>];
        // 删除指定行
        //[tableView deleteRowsAtIndexPaths:<#(NSArray *)#> withRowAnimation:<#(UITableViewRowAnimation)#>];
        // 插入新的行

        NSIndexPath *newPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0];
        [tableView insertRowsAtIndexPaths:@[newPath] withRowAnimation:UITableViewRowAnimationTop];
    }

}
  • 排序

    如果实现了数据源UITableViewDataSource的moveRowAtIndexPath方法,就会有排序拖动功能,但这只是界面表面的变化,数据顺序并没有变,所以在这个方法中,需要改变数据顺序。
#pragma mark 如果实现了这个方法, 就会有排序功能
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    // 取出即将要删除的数据
    NSString *data = self.mydata[sourceIndexPath.row];

    // 删除需要移动的那一行
    [self.mydata removeObject:data];

    // 插入之前删除的数据
    [self.mydata insertObject:data atIndex:destinationIndexPath.row];
}
  • 4个刷新UI界面的方法

    添加新的行:

    [tableView insertRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationTop];

    删除指定的行:

    [tableView deleteRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationTop]

    局部刷新指定的行:

    [tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationTop]

    整体刷新所有的行:

    [tableView reloadData]

10.8 自定义Cell

  • 步骤:

    ①、新建xid来描述Cell(拖一个UITableViewCell设置宽高以及identify);

    ②、实现数据源和代理方法,返回数据和Cell的高度(heightForRowAtIndexPath );

    ③、拿到Cell中的子控件绑定数据(根据tag);

    ④、拿到按钮绑定监听器(根据事件取得事件点对应的行号)
#pragma mark 每当有一个cell进入视野范围内就会调用,返回当前这行显示的cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 0.用static修饰的局部变量,只会初始化一次
    static NSString *ID = @"Cell";

    // 1.拿到一个标识先去缓存池中查找对应的Cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 2.如果缓存池中没有,才需要传入一个标识创建新的Cell
    if (cell == nil) {
        // 通过 xib文件来加载cell
        NSBundle *bundle = [NSBundle mainBundle];
        NSArray * objs = [bundle loadNibNamed:@"BookCell" owner:nil options:nil];
        cell = [ objs lastObject];

        // 绑定监听器
        UIButton *collect  = (UIButton *)[cell viewWithTag:3];
        [collect addTarget:self action:@selector(collectBook:event:) forControlEvents:UIControlEventTouchUpInside];
    }

    //cell.tag = indexPath.row + 1000;

    // 3.覆盖数据
    // 3.1 取出book对象
    Book *book = self.books[indexPath.row];
    // 3.2 设置名称
    UILabel *nameLabel = (UILabel *)[cell viewWithTag:1];
    nameLabel.text = book.name;

    return cell;
}
  • cell中的按钮添加点击事件

    通过点击事件点的位置,确定cell的索引:
// 得到触摸事件对象 --->  得到触摸点  ---> 得到触摸点在UITableView中的位置 ---> 得到触摸点在UITableView中的行号
- (void)collectBook:(UIButton *) btn event:(UIEvent *)event
{
    //NSLog(@"----%@", event);
    UITableView *tableView = (UITableView *)self.view;

    // 获取所有的触摸点(UITouch对象,如果是单点触碰,就只有1个UITouch)
    NSSet *touches = [event allTouches];

    // 一个UITouch对象对应一根手指
    UITouch *touch = [touches anyObject];

    // 获取触摸点在UITableView上面的的位置
    CGPoint position = [touch locationInView:tableView];

    // 根据触摸位置 得到 对应的行号
    NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:position];
    //NSLog(@"%d", indexPath.row);

    Book *book = self.books[indexPath.row];
    NSLog(@"%@", book.name);
}
  • Cell属性封装:

    根据tag找控件(android根据id找控件),tag太多不好管理,效率不高,如果控件太多代码量大:

    定义一个类继承UITableViewCell,xid指向这个类,子控件作为此类的属性即可,创建Cell时用这个类接受:

iOS常见控件的基本使用

10.9 九宫格

iOS中没有Android中的GridView,九宫格是由UITableView实现的,自定义Cell,每一行Cell中放入numClunk(多少列) 个按钮。

10.10 qq联系人列表

iOS中没有Android中的ExpandListView,如果需要实现分组展开合并功能,只需要控制返回那个分组返回的行数,行数为0就是合并了。

  • 自定义分组标题
#pragma mark 第section组对应的标题
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    HeaderView *titleView = [HeaderView buttonWithType:UIButtonTypeCustom];

    // 设置标题内容
    NSDictionary *dict = self.allFriends[section];
    [titleView setTitle:dict[@"group"] forState:UIControlStateNormal];

    // 设置标题颜色
    [titleView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

    // 设置背景颜色
    [titleView setBackgroundColor:[UIColor grayColor]];

    // 设置按钮的tag为组号
    titleView.tag = section;

    // 监听标题点击
    [titleView addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside];

    // 取出第section组的状态
    int result = [self.status[@(section)] intValue];
    // 对状态进行取反
    if (result == 0) {
        titleView.imageView.transform = CGAffineTransformIdentity;
    } else {
        titleView.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
    }

    return titleView;
}
  • 合并展开分组
#pragma mark - 监听标题点击
- (void)titleClick:(UIButton *)btn {
    // 取出标题按钮对应的组号
    int section = btn.tag;

    // 取出第section组的状态
    int result = [self.status[@(section)] intValue];

    // 对状态进行取反
    if (result == 0) {
        [self.status setObject:@1 forKey:@(section)];
    } else {
        [self.status setObject:@0 forKey:@(section)];
    }

    // 刷新数据(会重新给数据源发送消息)
    [self.tableView reloadData];
}

//- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
//{
//    // 1.获取这组对应的字典数据
//    NSDictionary *dict = self.allFriends[section];
//
//    return dict[@"group"];
//}

#pragma mark 每一组中有多少行数据
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // 取出第section组对应的状态
    int result = [self.status[@(section)] intValue];

    if (result == 0) { // 合并状态
        return 0;
    } else { // 展开状态
        // 1.先获取这组对应的数据
        NSDictionary *group = self.allFriends[section];
        // 2.获取这组里面的好友
        NSArray *friends = group[@"friends"];
        return friends.count;
    }
}