UIButton,UIScrollView,UITableView常见属性,使用案例 - iOS

IBAction:
1> 能保证方法可以连线
2> 相当于void

IBOutlet:
1> 能保证属性可以连线

下面列举一些在开发中可能用得上的UI控件

  • UIButton 按钮
  • UILabel 文本标签
  • UITextField 文本输入框
  • UIImageView 图片显示
  • UIProgressView 进度条
  • UISlider 滑块
  • UISwitch 开关
  • UISegmentControl 选项卡
  • UIActivityIndicator 圈圈
  • UIAlertView 对话框(中间弹框)
  • UIActionSheet 底部弹框
  • UIScrollView 滚动的控件
  • UIPageControl 分页控件
  • UITextView 能滚动的文字显示控件
  • UITableView 表格
  • UICollectionView 九宫格
  • UIPickerView 选择器
  • UIDatePicker 日期选择器
  • UIWebView 网页显示控件
  • UIToolbar 工具条
  • UINavigationBar导航条









































如何修改控件状态

那如何去修改控件的状态呢?方法很简单

  • 每一个UI控件都是一个对象
  • 修改UI控件的状态,其实就是修改控件对象的属性
  • 比如修改UILabel显示的文字,就修改UILabel对象的text属性即可
  • 比如修改UIImageView显示的图片,就修改UIImageView对象的image属性即可

不难想到,每一个UI控件肯定都有很多属性,比如:

  • UIProgressView进度条控件有progress属性(进度值)
  • UILabel和UITextField都有text属性(显示文字)
  • … …

虽然,每一个UI控件都有自己的独特属性,但是有些属性是每个UI控件都具备的,比如每一个UI控件都有自己的位置和尺寸、都有自己的父控件、子控件。于是,所有的UI控件最终都继承自UIView,UI控件的公共属性都定义在UIView中,比如:

  • frame :位置和尺寸
  • center :中心点位置
  • … …

UIView的常见属性

  • @property(nonatomic,readonly) UIView *superview;
  • 获得自己的父控件对象

  • @property(nonatomic,readonly,copy) NSArray *subviews;

  • 获得自己的所有子控件对象

  • @property(nonatomic) NSInteger tag;

  • 控件的ID\标识,父控件可以通过tag来找到对应的子控件

  • @property(nonatomic) CGAffineTransform transform;

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

  • @property(nonatomic) CGRect frame;

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

  • @property(nonatomic) CGRect bounds;

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

  • @property(nonatomic) CGPoint center;

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

UIView的常见方法

  • - (void)addSubview:(UIView *)view;
    添加一个子控件view

  • - (void)removeFromSuperview;
    从父控件中移除

  • - (UIView *)viewWithTag:(NSInteger)tag;
    根据一个tag标识找出对应的控件(一般都是子控件)

代码创建按钮

在开发过程中,并不是每次都通过storyboard拖控件完成UI界面,因为storyboard上面的界面是“固定死”的,有时候可能会在程序运行过程中动态地添加一些新的控件到界面上

比如QQ的聊天信息,是有人发出一条信息后才动态显示出来的。因此,需要掌握如何用代码动态地添加控件

实际上,storyboard的本质就是根据图形界面描述转成相应的代码

下面演示用代码创建按钮

// 1.创建一个自定义的按钮
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];

// 2.添加按钮
[self.view addSubview:btn];

// 3.设置按钮的位置和尺寸
btn.frame = CGRectMake(100, 100, 100, 100);

// 4.监听按钮点击(点击按钮后就会调用self的btnClick方法)
[btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];

// 5.设置按钮在默认状态下的属性
// 5.1.默认状态的背景
[btn setBackgroundImage:[UIImage imageNamed:@"btn_01"] forState:UIControlStateNormal];
// 5.2.默认状态的文字
[btn setTitle:@"点我啊" forState:UIControlStateNormal];
// 5.3.默认状态的文字颜色
[btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];

// 6.设置按钮在高亮状态下的属性
// 6.1.高亮状态的背景
[btn setBackgroundImage:[UIImage imageNamed:@"btn_02"] forState:UIControlStateHighlighted];
// 6.2.高亮状态的文字
[btn setTitle:@"高亮显示" forState:UIControlStateHighlighted];
// 6.3.高亮状态的文字颜色
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateHighlighted];

UIButton的状态

normal(普通状态)

  • 默认情况(Default)
  • 对应的枚举常量:UIControlStateNormal

highlighted(高亮状态)

  • 按钮被按下去的时候(手指还未松开)
  • 对应的枚举常量:UIControlStateHighlighted

disabled(失效状态,不可用状态)

  • 如果enabled属性为NO,就是处于disable状态,代表按钮不可以被点击
  • 对应的枚举常量:UIControlStateDisabled

实现简单动画

在iOS开发中,想实现一些小动画是非常容易的

  • 系统会根据某个属性值的改变自动形成动画
  • 比如x值本来是10,然后x值突然改为了100,系统会通过平移动画的方式让x值慢慢从10变到100

简易动画大致有2种方式:

  • 头尾式
[UIView beginAnimations:nil context:nil];
/** 需要执行动画的代码 **/
[UIView commitAnimations];
  • Block式
[UIView animateWithDuration:0.5 animations:^{
    /** 需要执行动画的代码 **/
}];

修改控件的位置和尺寸

通过以下属性可以修改控件的位置

  • frame.origin
  • center

通过以下属性可以修改控件的尺寸

  • frame.size
  • bounds.size

transform属性

利用transform属性可以修改控件的位移(位置)、缩放、旋转

创建一个transform属性

CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,  CGFloat ty) ;
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
// (angle是弧度制,并不是角度制)

在某个transform的基础上进行叠加

CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);
CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);

清空之前设置的transform属性

view.transform = CGAffineTransformIdentity;

常见类型

一个UIColor代表一种颜色,通过UIColor的类方法,可以获得很多常用的颜色

+ (UIColor *)blackColor;      // 0.0 white 黑色
+ (UIColor *)darkGrayColor;   // 0.333 white 深灰色
+ (UIColor *)lightGrayColor;  // 0.667 white 亮灰色
+ (UIColor *)whiteColor;      // 1.0 white 白色
+ (UIColor *)grayColor;       // 0.5 white 灰色
+ (UIColor *)redColor;        // 1.0, 0.0, 0.0 RGB 红色
+ (UIColor *)greenColor;      // 0.0, 1.0, 0.0 RGB 绿色
+ (UIColor *)blueColor;       // 0.0, 0.0, 1.0 RGB 蓝色
+ (UIColor *)cyanColor;       // 0.0, 1.0, 1.0 RGB 青色
+ (UIColor *)yellowColor;     // 1.0, 1.0, 0.0 RGB 黄色
+ (UIColor *)magentaColor;    // 1.0, 0.0, 1.0 RGB 品红
+ (UIColor *)orangeColor;     // 1.0, 0.5, 0.0 RGB 橙色
+ (UIColor *)purpleColor;     // 0.5, 0.0, 0.5 RGB 紫色
+ (UIColor *)brownColor;      // 0.6, 0.4, 0.2 RGB 棕色
+ (UIColor *)clearColor;      // 0.0 white, 0.0 alpha 清除颜色(空色)

在用代码创建按钮的同时指定按钮样式

UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 



UIButton和UIImageView

相同点:都能显示图片

不同点

  • UIButton默认情况就能监听点击事件,而UIImageView默认情况下不能
  • UIButton可以在不同状态下显示不同的图片
  • UIButton既能显示文字,又能显示图片

如何选择

  • UIButton:需要显示图片,点击图片后需要做一些特定的操作
  • UIImageView:仅仅需要显示图片,点击图片后不需要做任何事情

NSArray和NSDictionary的使用

  • 当图片内容非常多时,“根据index来设置内容”的代码就不具备扩展性,要经常改动

  • 为了改变现状,可以考虑讲图片数据线保存到一个数组中,数组中有序地放着很多字典,一个字典代表一张图片数据,包含了图片名、图片描述

@property (strong, nonatomic) NSArray *images;
  • 由于只需要初始化一次图片数据,因此放在get方法中初始化

  • 将属性放在get方法中初始化的方式,称为“懒加载”\”延迟加载”

什么是Plist文件

  • 直接将数据直接写在代码里面,不是一种合理的做法。如果数据经常改,就要经常翻开对应的代码进行修改,造成代码扩展性低

  • 因此,可以考虑将经常变的数据放在文件中进行存储,程序启动后从文件中读取最新的数据。如果要变动数据,直接修改数据文件即可,不用修改代码

  • 一般可以使用属性列表文件存储NSArray或者NSDictionary之类的数据,这种属性列表文件的扩展名是plist,因此也成为“Plist文件”

解析Plist文件

接下来通过代码来解析Plist文件中的数据

  • 获得Plist文件的全路径
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:@"imageData" ofType:@"plist"];
  • 加载plist文件
_images = [NSArray arrayWithContentsOfFile:path];

- (NSArray *)images
{
    if (_images == nil) {
        NSBundle *bundle = [NSBundle mainBundle];
        NSString *path = [bundle pathForResource:@"imageData" ofType:@"plist"];
        _images = [NSArray arrayWithContentsOfFile:path];
    }
    return _images;
}

NSBundle

1> 一个NSBundle代表一个文件夹,利用NSBundle能访问对应的文件夹

2> 利用mainBundle就可以访问软件资源包中的任何资源

3> 模拟器应用程序的安装路径

汤姆猫

连续播放多张图片


#import "MJViewController.h"

@interface MJViewController ()
- (IBAction)drink;
- (IBAction)knock;
- (IBAction)rightFoot;

/** 这是一只显示图片的猫 */
@property (weak, nonatomic) IBOutlet UIImageView *tom;

@end

@implementation MJViewController
/** 播放动画 */
- (void)runAnimationWithCount:(int)count name:(NSString *)name
{
    if (self.tom.isAnimating) return;

    // 1.加载所有的动画图片
    NSMutableArray *images = [NSMutableArray array];

    for (int i = 0; i<count; i++) {
        // 计算文件名
        NSString *filename = [NSString stringWithFormat:@"%@_%02d.jpg", name, i];
        // 加载图片

        // imageNamed: 有缓存(传入文件名)
//        UIImage *image = [UIImage imageNamed:filename];

        // imageWithContentsOfFile: 没有缓存(传入文件的全路径)
        NSBundle *bundle = [NSBundle mainBundle];
        NSString *path = [bundle pathForResource:filename ofType:nil];
        UIImage *image = [UIImage imageWithContentsOfFile:path];

        // 添加图片到数组中
        [images addObject:image];
    }
    self.tom.animationImages = images;

    // 2.设置播放次数(1次)
    self.tom.animationRepeatCount = 1;

    // 3.设置播放时间
    self.tom.animationDuration = images.count * 0.05;

    [self.tom startAnimating];

    // 4.动画放完1秒后清除内存
    CGFloat delay = self.tom.animationDuration + 1.0;
    [self.tom performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:delay];
//    [self performSelector:@selector(clearCache) withObject:nil afterDelay:delay];
}

//- (void)clearCache
//{
    self.tom.animationImages = nil;
//    
//    [self.tom setAnimationImages:nil];
//}

- (IBAction)drink {
    [self runAnimationWithCount:81 name:@"drink"];

//    if (self.tom.isAnimating) return;
//    
//    // 1.加载所有的动画图片
//    NSMutableArray *images = [NSMutableArray array];
//    
//    for (int i = 0; i<81; i++) {
//        // 计算文件名
//        NSString *filename = [NSString stringWithFormat:@"drink_%02d.jpg", i];
//        // 加载图片
//        UIImage *image = [UIImage imageNamed:filename];
//        // 添加图片到数组中
//        [images addObject:image];
//    }
//    self.tom.animationImages = images;
//    
//    // 2.设置播放次数(1次)
//    self.tom.animationRepeatCount = 1;
//    
//    // 3.设置播放时间
//    self.tom.animationDuration = images.count * 0.05;
//    
//    [self.tom startAnimating];
}

- (IBAction)knock {
    [self runAnimationWithCount:81 name:@"knockout"];
}

- (IBAction)rightFoot {
    [self runAnimationWithCount:30 name:@"footRight"];
}
@end

文档注释的写法:

/** 这是一只显示图片的猫 */

这样写可以在调用的时候显示注释

苹果API常用英语名词

  1. indicating 决定
  2. in order to 以便
  3. rectangle bounds 矩形尺寸
  4. applied 应用
  5. entirety 全部
  6. technique 方法
  7. truncating 截短
  8. wrapping 换行
  9. string 字符串
  10. familiar style 简体
  11. The styled text 主题样式
  12. Constants 常量
  13. Attribute 属性
  14. Consecutive 连续
  15. Shrink 收缩
  16. Discussion 详述
  17. Rendering 渲染
  18. Pasting 粘贴
  19. Prohibits 禁止
  20. Albeit 虽然
  21. Particular 特殊的,详细的
  22. Specify 指定
  23. Times 次数
  24. In that order 在这个命令下
  25. Passing 传递
  26. Determines 决定
  27. Resize 调整大小
  28. Comprises 包含
  29. Positive 正数
  30. Negative 负数
  31. Reverse 反转
  32. Valid 有效的
  33. Configure 设定
  34. Instead 相反
  35. Primarily 主要
  36. Obvious 非常
  37. Divergence 区别
  38. Conceptual 概念
  39. The model-view-controller design pattern MVC
  40. clearly 明显
  41. encapsulate 封装
  42. transparent 透明
  43. opaque 不透明
  44. momentary 瞬间
  45. diagonal 斜线的
  46. lifts their finger 抬起手指
  47. rest 停止
  48. reckoning 估算
  49. countDown 倒计时
  50. evenly 均匀的
  51. designation 名称
  52. trim 裁剪
  53. simulate 模拟
  54. incompatible 不匹配

用模型取代字典的好处

使用字典的坏处

  • 一般情况下,设置数据和取出数据都使用“字符串类型的key”,编写这些key时,编译器不会有任何友善提示,需要手敲

dict[@"name"] = @"Jack";
NSString *name = dict[@"name"];
  • 手敲字符串key,key容易写错
  • Key如果写错了,编译器不会有任何警告和报错,造成设错数据或者取错数据

使用模型的好处

  • 所谓模型,其实就是数据模型,专门用来存放数据的对象,用它来表示数据会更加专业
  • 模型设置数据和取出数据都是通过它的属性,属性名如果写错了,编译器会马上报错,因此,保证了数据的正确性
  • 使用模型访问属性时,编译器会提供一系列的提示,提高编码效率
app.name = @"Jack”;
NSString *name = app.name;

字典转模型

  • 字典转模型的过程最好封装在模型内部

  • 模型应该提供一个可以传入字典参数的构造方法

- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)xxxWithDict:(NSDictionary *)dict;
- (void)setValue:(id)value forUndefinedKey:(nonnull NSString *)key
{
    if ([key isEqualToString:@"id"]) {
        self.ID = value;
    }
}

- (instancetype)initWithDict:(NSDictionary *)dict
{
    if (self = [super init]) {
        [self setValuesForKeysWithDictionary:dict];
    }

    return self;
}

+ (instancetype)recipeWithDict:(NSDictionary *)dict
{
    return [[self alloc] initWithDict:dict];
}
for (NSDictionary *dict in dataDict) {
    RecipeModel *model = [RecipeModel recipeWithDict:dict];

    //            NSLog(@"%@", model);

    [self.dataArray addObject:model];
}

instancetype

  • instancetype在类型表示上,跟id一样,可以表示任何对象类型

  • instancetype只能用在返回值类型上,不能像id一样用在参数类型上

  • instancetype比id多一个好处:编译器会检测instancetype的真实类型

Xib文件的使用

Xib文件可以用来描述某一块局部的UI界面

Xib文件的加载

  • 方法1
NSArray *objs = [[NSBundle mainBundle] loadNibNamed:@"MJAppView" owner:nil options:nil];

这个方法会创建xib中的所有对象,并且将对象按顺序放到objs数组中
(如果xib如右图所示,那么objs数组中依次会有3个对象:1个UIView、1个UIButton、1个UISwitch)

  • 方法2

bundle参数可以为nil,默认就是main bundle

UINib *nib = [UINib nibWithNibName:@"MJAppView" bundle:[NSBundle mainBundle]];
NSArray *objs = [nib instantiateWithOwner:nil options:nil];
  • 在开发阶段,面向开发者的是xib文件; 当把应用装到手机上时,xib文件就会转为nib文件

UIScrollView

  • 移动设备的屏幕大小是极其有限的,因此直接展示在用户眼前的内容也相当有限

  • 当展示的内容较多,超出一个屏幕时,用户可通过滚动手势来查看屏幕以外的内容

  • 普通的UIView不具备滚动功能,不能显示过多的内容

  • UIScrollView是一个能够滚动的视图控件,可以用来展示大量的内容,并且可以通过滚动查看所有的内容

  • 举例:手机上的“设置”、其他示例程序

UIScrollView的基本使用

UIScrollView的用法很简单

  • 将需要展示的内容添加到UIScrollView中
  • 设置UIScrollView的contentSize属性,告诉UIScrollView所有内容的尺寸,也就是告诉它滚动的范围(能滚多远,滚到哪里是尽头)

UIScrollView无法滚动的解决办法

如果UIScrollView无法滚动,可能是以下原因:

  • 没有设置contentSize
  • scrollEnabled = NO
  • 没有接收到触摸事件:userInteractionEnabled = NO
  • 没有取消autolayout功能(要想scrollView滚动,必须取消autolayout)

UIScrollView和控制器

一般情况下,就设置UIScrollView所在的控制器 为 UIScrollView的delegate

设置控制器为UIScrollView的delegate有2种方法:

  • 通过代码(self就是控制器)
self.scrollView.delegate = self;
  • 通过storyboard拖线(右击UIScrollView)

UIScrollView的常见属性

@property(nonatomic) CGPoint contentOffset; 

这个属性用来表示UIScrollView滚动的位置

@property(nonatomic) CGSize contentSize;

这个属性用来表示UIScrollView内容的尺寸,滚动范围(能滚多远)

@property(nonatomic) UIEdgeInsets contentInset; 

这个属性能够在UIScrollView的4周增加额外的滚动区域

UIScrollView的代理(delegate)

  • 很多时候,我们想在 UIScrollView 正在滚动 或 滚动到某个位置 或者 停止滚动 时做一些特定的操作
  • 要想完成上述功能,前提条件就是能够监听到 UIScrollView 的整个滚动过程
  • 当 UIScrollView 发生一系列的滚动操作时, 会自动通知它的代理(delegate)对象,给它的代理发送相应的消息,让代理得知它的滚动情况
  • 也就是说,要想监听 UIScrollView 的滚动过程,就必须先给 UIScrollView 设置一个代理对象,然后通过代理得知 UIScrollView 的滚动过程
// 用户开始拖拽时调用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
// 滚动到某个位置时调用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
// 用户结束拖拽时调用
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

内容缩放

UIScrollView不仅能滚动显示大量内容,还能对其内容进行缩放处理

也就是说,要完成缩放功能的话,只需要将需要缩放的内容添加到UIScrollView中

UIScrollView的缩放原理

当用户在UIScrollView身上使用捏合手势时,UIScrollView会给代理发送一条消息,询问代理究竟要缩放自己内部的哪一个子控件(哪一块内容)

// 用户使用捏合手势时调用
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;

当用户在 UIScrollView 身上使用捏合手势时,UIScrollView 会调用代理的 viewForZoomingInScrollView: 方法,这个方法返回的控件就是需要进行缩放的控件

缩放实现步骤

  1. 设置UIScrollView的id delegate代理对象
  2. 设置minimumZoomScale :缩小的最小比例
  3. 设置maximumZoomScale :放大的最大比例
  4. 让代理对象实现下面的方法,返回需要缩放的视图控件
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;

跟缩放相关的其他代理方法

  • 缩放完毕的时候调用
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
  • 正在缩放的时候调用
- (void)scrollViewDidZoom:(UIScrollView *)scrollView

分页

只要将 UIScrollView的pageEnabled 属性设置为 YES ,UIScrollView 会被分割成多个独立页面,里面的内容就能进行分页展示

一般会配合 UIPageControl 增强分页效果,UIPageControl 常用属性如下

  • 一共有多少页
@property(nonatomic) NSInteger numberOfPages;
  • 当前显示的页码
@property(nonatomic) NSInteger currentPage; 
  • 只有一页时,是否需要隐藏页码指示器
@property(nonatomic) BOOL hidesForSinglePage; 
  • 其他页码指示器的颜色
@property(nonatomic,retain) UIColor *pageIndicatorTintColor;
  • 当前页码指示器的颜色
@property(nonatomic,retain) UIColor *currentPageIndicatorTintColor;

NSTimer

NSTimer叫做“定时器”,它的作用如下

  • 在指定的时间执行指定的任务
  • 每隔一段时间执行指定的任务

调用下面的方法就会开启一个定时任务

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                                     target:(id)target
                                   selector:(SEL)aSelector
                                   userInfo:(id)userInfo
                                    repeats:(BOOL)repeats

每隔ti秒,调用一次aTarget的aSelector方法,yesOrNo决定了是否重复执行这个任务

通过invalidate方法可以停止定时器的工作,一旦定时器被停止了,就不能再次执行任务。只能再创建一个新的定时器才能执行新的任务

- (void)invalidate;

UITableView

在众多移动应用中,能看到各式各样的表格数据

在iOS中,要实现表格数据展示,最常用的做法就是使用UITableView

UITableView继承自UIScrollView,因此支持垂直滚动,而且性能极佳

如何展示数据

  • UITableView需要一个数据源(dataSource)来显示数据

  • UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等

  • 没有设置数据源的UITableView只是个空壳

  • 凡是遵守UITableViewDataSource协议的OC对象,都可以是UITableView的数据源

UITableViewDataSource协议

// 一共有多少组数据
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
// 每一组有多少行数据
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
// 每一行显示什么内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@property (nonatomic, assign) id <UITableViewDataSource> dataSource;

tableView展示数据的过程

调用数据源的下面方法得知一共有多少组数据

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;

调用数据源的下面方法得知每一组有多少行数据

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

调用数据源的下面方法得知每一行显示什么内容

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

Cell简介

UITableView的每一行都是一个UITableViewCell,通过dataSource的tableView:cellForRowAtIndexPath:方法来初始化每一行

UITableViewCell内部有个默认的子视图:contentView,contentView是UITableViewCell所显示内容的父视图,可显示一些辅助指示视图

辅助指示视图的作用是显示一个表示动作的图标,可以通过设置UITableViewCell的accessoryType来显示,默认是UITableViewCellAccessoryNone(不显示辅助指示视图),其他值如下:

还可以通过cell的accessoryView属性来自定义辅助指示视图(比如往右边放一个开关)

Cell的重用原理

iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象

重用原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象

还有一个非常重要的问题:有时候需要自定义UITableViewCell(用一个子类继承UITableViewCell),而且每一行用的不一定是同一种UITableViewCell,所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,那么UITableView在重用UITableViewCell时可能会得到错误类型的UITableViewCell

解决方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化一个UITableViewCell对象

Cell的重用代码

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.定义一个cell的标识
    static NSString *ID = @"mjcell";

    // 2.从缓存池中取出cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 3.如果缓存池中没有cell
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }

    // 4.设置cell的属性...

    return cell;
}

使用xib封装一个view的步骤

  1. 新建一个xib文件描述一个view的内部结构(假设叫做MJTgCell.xib)
  2. 新建一个自定义的类(自定义类需要继承自系统自带的view, 继承自哪个类, 取决于xib根对象的Class)
  3. 新建类的类名最好跟xib的文件名保持一致(比如类名就叫做MJTgCell)
  4. 将xib中的控件 和 自定义类的.m文件 进行连线
  5. 提供一个类方法返回一个创建好的自定义view(屏蔽从xib加载的过程)
  6. 提供一个模型属性让外界传递模型数据
  7. 重写模型属性的setter方法,在这里将模型数据展示到对应的子控件上面

通过代码自定义cell(cell的高度不一致)

1.新建一个继承自UITableViewCell的类

2.重写initWithStyle:reuseIdentifier:方法

  • 添加所有需要显示的子控件(不需要设置子控件的数据和frame, 子控件要添加到contentView中)
  • 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)

3.提供2个模型

  • 数据模型: 存放文字数据\图片数据
  • frame模型: 存放数据模型\所有子控件的frame\cell的高度

4.cell拥有一个frame模型(不要直接拥有数据模型)

5.重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame

6.frame模型数据的初始化已经采取懒加载的方式(每一个cell对应的frame模型数据只加载一次)

Delegate的使用场合

  • 对象A内部发生了一些事情,想通知对象B
  • 对象B想监听对象A内部发生了什么事情
  • 对象A想在自己的方法内部调用对象B的某个方法,并且对象A不能对对象B有耦合依赖
  • 对象A想传递数据给对象B
  • ……

以上情况,结果都一样:对象B是对象A的代理(delegate)

使用delegate的步骤

先搞清楚谁是谁的代理(delegate)

定义代理协议,协议名称的命名规范:控件类名 + Delegate

定义代理方法

  • 代理方法一般都定义为@optional
  • 代理方法名都以控件名开头
  • 代理方法至少有1个参数,将控件本身传递出去

设置代理(delegate)对象 (比如myView.delegate = xxxx;)

  • 代理对象遵守协议
  • 代理对象实现协议里面该实现的方法

在恰当的时刻调用代理对象(delegate)的代理方法,通知代理发生了什么事情
(在调用之前判断代理是否实现了该代理方法)

新博客文章地址:UIButton,UIScrollView,UITableView常见属性,使用案例

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值