大家好,今天来为大家解答深入解析iOS应用开发技巧与最佳实践这个问题的一些问题点,包括也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~
为什么要在initWithFrame:方法而不是在init方法?因为自定义类是使用纯代码创建的,所以以后使用的时候可能会使用init方法或者initWithFrame:方法创建,但无论使用哪种方法,最终都会调用initWithFrame:方法。通过该方法创建子控件可以保证无论使用哪种方法都可以成功创建子控件。
为什么要在initWithFrame:方法里面只是将子控件加到view而不设置尺寸?前面提到过,这两个方法最终都会调用initWithFrame:方法。如果使用init 方法创建,这个视图的框架可能是不确定的:
CYLView *view=[[CYLView alloc] init];
view.frame=CGRectMake(0, 0, 100, 100);
.如果是这样的话,那么在init方法中,帧是不确定的。这时如果在initWithFrame:方法中设置了尺寸,那么每个子控件的尺寸都会为0,因为这个视图的frame还没有设置。 (可以看到是在发送init消息后设置的)
所以我们应该在layoutSubviews方法中使用保证view的frame设置完才会设置它的子控件的尺寸。来达到这个目的。这个方法会在view__即将显示__的时候被调用,之后当view的大小(不是位置)发生变化时,这个方法也会被调用。
因此,正常的做法应该是在initWithFrame:方法中创建子控件。注意,此时子控件可能只是一个局部变量,所以如果要在layoutSubviews中访问它,一般需要创建这个子控件对应的属性来指向它。
@property(非原子,弱)UIButton *按钮; //注意,这里可以使用weak,因为button已经被添加到了self.view.subviews数组中。
.
- (instancetype)initWithFrame: (CGRect)frame
{
if (self=[超级initWithFrame: 帧]) {
UIButton *button=. //创建一个按钮
[button setTitle:] //设置按钮属性
[self.view addSubview: 按钮]; //将按钮添加到视图中,但不设置大小
self.button=按钮; //将self.button 指向此按钮,以确保可以在layoutSubviews中访问它
UILabel *label=. //其他子控件同理
}
}这样我们就可以访问layoutSubviews中的子控件并设置子控件的大小了,因为此时视图的frame已经确定了。
- (void)布局子视图
{
[超级布局子视图]; //注意,别忘了调用父类的layoutSubviews方法!
self.button.frame=. //设置按钮的框架
self.label.frame=. //设置标签的框架
}经过上述步骤后,就可以实现自定义控件了。
同时我们也希望给我们自定义的控件数据并让它显示出来。
一般来说,要先将获取到的数据转换为模型数据,然后再将模型数据传递给自定义控件进行显示。
所以在这个自定义控件的头文件中,我们需要设置获取别人传入的数据的接口。例如,我们目前有一个Book 类,它有一个name 属性来显示名称,还有一个like 属性来显示有多少人喜欢。现在我们需要在自定义类的标签子控件上显示Book的名称,并在自定义类的按钮子控件上显示Book的like。
首先在自定义类的头文件中:
.
@property(非原子,强)书*书;
.这里我们收到一本书作为需要显示的数据。
然后在自定义实现文件中重写书中的setter方法:
- (void)setBook: (书*)书
{
_book=书; //注意,在这个方法中,不写这句话是没有问题的,因为下面的语句使用的是book而不是self.book或者_book,但是如果你还想在其他方法中访问book属性就需要写,否则self.book 或_book 将始终为nil (因为该书在此方法的范围之外被销毁,如果要再次访问它,则需要有其他引用指向它)。所以我建议你写下这句话。
[self.button setTitle: book.like forState.];
self.label=书名.
这样,当我们想要使用自定义类显示数据时:
//在控制器类的方法中:
书*book=self.books[index]; //这是指获取books数据中的一些数据进行显示
CYLView *view=[[CYLView alloc] initWithFrame:];
[self.view addSubview:视图]; //添加自定义类来查看
view.book=书; //设置书籍数据。这时候就会调用setter方法来设置各个控件的数据。这样就实现了自定义类显示数据的功能。而且,通过将子控件封装成自定义,控制器只需要创建一个自定义的类并给它数据,而不需要关心该类内部是如何设计的,有哪些控件,以及数据是如何排列的,所以当需求发生变化的时候,我们的控制器可能根本不需要改变,只需要改变自定义类的内部即可。
总结:
在initWithFrame:中添加子控件。
在layoutSubviews中设置子控件框架。
设置外部数据接口,重写setter方法,设置子控件的显示数据。
使用视图控制器中的init/initWithFrame: 方法创建自定义类,并为自定义类的框架赋值。
只需要给自定义类暴露的数据接口赋值即可。
使用xib方式
使用xib可以省去initWithFrame:和layoutSubviews中添加子控件和设置子控件大小的步骤,以及在视图控制器中设置视图的frame,因为添加子控件和设置大小子控件和整个视图的尺寸已经在xib中完成。 (注意整个view的位置还没有设置,需要在controller中设置。)
我们只需要向外界提供数据接口,并重写setter方法即可显示数据。
注意xib中的类一定要设置为我们的自定义类,这样创建的自定义类就不是默认的父类。
当然,使用xib方法需要加载xib文件。加载xib文件有两种方法:
//第一种方法(比较常用)
CYLView *view=[[[NSBundle mainBundle] loadNibNamed:@"CYLView"owner:nil options:nil]firstObject]; //CYLView代表CYLView.xib,代表CYLView类对应的xib文件。该方法返回一个NSArray。我们取第一个Object或者最后一个(因为这个数组只有一个CYLView,没有其他对象)就是需要加载的CYLView。
//第二种方法
UINib *nib=[UINib nibWithNibName:@"CYLView"bundle:nil];
NSArray *objectArray=[nib instantiateWithOwner:nil options:nil];
CYLView *view=[objectArrayfirstObject]; xib文件中的控件可以通过Control-Drag在CYLView中连接,以便CYLView可以访问这些控件。 (可以在setter方法中给这些控件赋值来显示数据)
总结:
创建一个xib,将需要添加的控件拖入xib中并设置大小。并将这个xib的Class设置为我们自定义的类。
使用IBOutlet 将xib 中的控件与自定义类关联。
设置外部数据接口,重写setter方法,设置子控件的显示数据。
在视图控制器类中加载xib文件,获取对应的类(这里不需要设置自定义类的frame,因为xib已经有了整个view的大小,只需要设置position即可) ,然后就可以从外部查看该类的Data接口赋值了。
补充
如果使用代码创建控件,那么会在创建时处调用initWithFrame:方法;如果使用xib/storyboard创建控件,那么会在创建时处调用initWithCoder:方法。
当访问initWithCoder:中的属性时,比如self.button,你会发现是nil,因为此时自定义控件正在初始化,self.button可能还没有被赋值(self.button是一个IBOutlet,而IBOutlet 本质上相当于Xcode 找到这个对应的属性,然后执行UIButton *button=.[self.view addSubview: Button] 等操作,而所有这些操作都相当于在CYLView *view=[[ 之后执行CYLView alloc] initWithCoder: nil] 方法(上面的代码相当于在storyboard中使用代码实现Xcode加载CYLView),所以如果在这个方法中进行初始化操作,可能会失败。
所以建议在awakeFromNib方法中进行初始化的额外操作。因为awakeFromNib是在初始化完成后调用的,所以在这个方法中访问属性(IBOutlet)可以保证不为nil。
其实,在使用xib创建自定义控件时,我们可以将加载xib的过程封装到一个自定义类中,只向外界暴露一个初始化方法,这样外界并不知道自定义控件内部是如何创建的。
例如,在CYLView.h中提供一个类工厂方法:
+ (instancetype)viewWithBook: (书*)书;然后在CYLView.m中实现该方法:
+ (instancetype)viewWithBook: (书*)书
{
CYLView *view=[[[NSBundle mainBundle] loadNibNamed: NSStringFromClass(self)owner: nil options: nil]firstObject];
view.book=书;
返回视图;
这样,外界只需要使用viewWithBook:方法传入一本书即可创建CYLView对象。具体如何创建只有CYLView知道。
如果我们想要初始化一些值,无论是通过代码还是xib,那么我们可以将初始化代码提取到一个方法中,然后分别在initWithFrame:方法和awakeFromNib方法中调用这个方法。
之前已经说过为什么是awakeFromNib:
通过xib创建的自定义控件需要设置IBOutlet属性。虽然会调用initWithCoder: 方法,但是调用该方法时IBOutlet 属性尚未设置,因此该方法中访问的属性将为nil。在awakeFromNib中,IBOutlet已经初始化,所以在该方法中初始化不会失败。
如果传递了initWithFrame:方法,则说明它是通过代码创建的自定义控件,其属性不是IBOutlet的,所以不存在未完成的IBOutlet的属性未初始化的情况。所以在initWithFrame:方法中访问一些属性是没有问题的。不过需要注意的是,如果通过init方法创建自定义控件,也会调用initWithFrame:方法,但此时self.frame并没有赋值(该方法时并没有设置控件的大小)被使用)。如果在这种情况下使用self.frame 没有任何价值。这种情况要注意。
文章到此结束,如果本次分享的深入解析iOS应用开发技巧与最佳实践和的问题解决了您的问题,那么我们由衷的感到高兴!
【深入解析iOS应用开发技巧与最佳实践】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
想要学习编程,iOS开发听起来很酷!
有12位网友表示赞同!
ios开发前景好 吗?
有15位网友表示赞同!
知道一些优秀的 iOS 开发工具吗?
有12位网友表示赞同!
最近想尝试 iOS 的项目,哪家公司比较好工作呢?
有20位网友表示赞同!
学习 iOS 开发需要哪些基础知识?
有9位网友表示赞同!
iOS 开发的市场竞争激烈吗?
有8位网友表示赞同!
有哪些学习 iOS 开发的线上课程比较推荐?
有13位网友表示赞同!
iOS 设计和开发这两块哪个更难?
有13位网友表示赞同!
有没有什么好玩的 iOS 应用程序开源,可以参考一下?
有17位网友表示赞同!
将来 iOS 会有更多新的技术吗?
有14位网友表示赞同!
iOS 开发的游戏怎么样玩?
有12位网友表示赞同!
我想做一个简单的 iOS 应用,要怎么开始学习?
有14位网友表示赞同!
苹果公司最近对 iOS 做了什么更新?
有12位网友表示赞同!
ios 开发薪资待遇如何?
有9位网友表示赞同!
学习 iOS 开发需要多长时间?
有7位网友表示赞同!
有哪些厉害的 iOS app 设计师?
有7位网友表示赞同!
想了解 iOS 编程语言 Swift?
有9位网友表示赞同!
苹果开发者社区有什么资源,可以帮助我们学习 iOS 开发?
有9位网友表示赞同!
有没有什么值得推荐的小众 iOS 开发工具?
有17位网友表示赞同!
我想参与到开源的 iOS 项目中,从哪里开始寻找呢?
有5位网友表示赞同!