IOS开发自定义view方法规范示例

目录
  • 前言
  • 一、关于自定义View的初始化方法
  • 二、关于addSubview
  • 三、关于layoutSubviews
  • 四、关于frame与bounds
  • 总结

前言

对于接触业务开发的童鞋,自定义View的开发是进行最频繁的工作了。但发现一些童鞋还是没有以一个好的规范甚至以一种错误的方式来搭建UI控件。由此,本文将以以下目录来进行讲叙,详细描述关于自定义View的一些书写注意事项。

  • 关于自定义View的初始化方法
  • 关于addSubview
  • 关于layoutSubviews
  • 关于frame与bounds

一、关于自定义View的初始化方法

通常我们会创建私有方法createUI方法来创建当前自定义View所需要的子View。那上述所说的createUI应该放在自定义View的哪个方法中呢?

1、init?

2、initWithFrame?

3、还是为了考虑外部创建自定义View的方式不同,在init与initWithFrame方法中均调用createUI方法?

我们来一一验证,首先在CustomView的init方法中调用createUI方法。

- (instancetype)init {
    if (self = [super init]) {
        [self createUI];
    }
    return self;
}
- (void)createUI {
    [self addSubview:self.testView];
}
- (UIView *)testView {
    if (!_testView) {
        _testView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        _testView.backgroundColor = [UIColor redColor];
    }
    return _testView;
}

外部以init形式创建CustomView

CustomView *customView = [[CustomView alloc] init];
customView.frame = CGRectMake(100, 100, 200, 200);
customView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:customView];

可验证,CustomView与其子视图均可正常显示。但有个问题是,如果外部以initWithFrame形式创建,无法调用createUI方法,因此子视图无法显示。

第二种初始化形式,单独在initWithFrame方法中调用createUI方法

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self createUI];
    }
    return self;
}

可验证结果是,无论外部以init或者initWithFrame方法初始化CustomView,均可以正常显示CustomView与其子视图。

最后我们做个实验,在init与initWithFrame方法中均调用createUI方法。调试createUI方法调用次数。

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self createUI];
    }
    return self;
}
- (instancetype)init {
    if (self = [super init]) {
        [self createUI];
    }
    return self;
}
- (void)createUI {
    NSLog(@"SubViews Add");
    [self addSubview:self.testView];
}
- (UIView *)testView {
    if (!_testView) {
        _testView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        _testView.backgroundColor = [UIColor redColor];
    }
    return _testView;
}
@end

外部创建CustomView仍使用init形式 通过打印结果或断点可验证createUI方法被执行了两次!

2019-06-26 17:17:51.961744+0800 TestAddSubview[72346:1989647] SubViews Add
2019-06-26 17:17:51.961917+0800 TestAddSubview[72346:1989647] SubViews Add

其实,上述三种假设均和一个问题相关,即自定义View的init方法是否会默认调用initWithFrame方法。

答案是肯定的,通过上述的代码调试流程,我们可以得到如下结论,关于代码的调用过程(以外部初始化init为例):

1、动态查找到CustomView的init方法

2、调用[super init]方法

3、super init方法内部执行的的是[super initWithFrame:CGRectZero]

4、若super发现CustomView实现了initWithFrame方法

5、转而执行self(CustomView)的initWithFrame方法

6、最后在执行init的其余部分

这里也可以验证一个结论:OC中的super实际上是让某个类去调用父类的方法,而不是父类去调用某个方法,方法动态调用过程顺序是由下而上的(这也是为什么只在init方法中进行createUI不会执行多次的原因,因为父类的initWithFrame没做createUI操作)。

结论: createUI方法最好在initWithFrame中调用,外部使用init或initWithFrame均可以正常执行createUI方法。不要在自定义View中同时重写init与initWithFrame并执行相同视图布局代码。会导致布局代码(createUI)执行多次。

二、关于addSubview

我们接着问题一自定义View的初始化方法来说,如果同时在init与initWithFrame中同时调用了createUI方法,会有什么影响呢?

显而易见的是createUI方法执行了多次,也就是说重复多次添加了self.testView。那是否会重复添加多个View层呢?

并不会,重复多次添加同一个View并不会产生多层级的情况。 我们看下addSubview的文档描述

This method establishes a strong reference to view and sets its next responder to the receiver, which is its new superview. Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview.

大概阐述的意思是,View有且仅有一个父视图,如果新的父视图与原父视图不一样,会将View在原视图中移除,添加到新视图上。

因此同一父视图重复添加同一个View并不会产生多层级。 可以简单通过代码验证,我们在createUI中循环添加self.testView,最终打印当前视图的子视图个数

- (void)createUI {
    for (NSInteger i = 0; i < 100; i++) {
        [self addSubview:self.testView];
    }
    NSLog(@"subviewsCount = 【%ld】",self.subviews.count);
    for (UIView *view in self.subviews) {
           NSLog(@"subView 【%@】",view);
    }
}

运行可见,视图的子视图个数始终为1

2019-06-28 16:02:50.420144+0800 TestAddSubview[78991:832644] subviewsCount = 【1】
2019-06-28 16:02:50.422151+0800 TestAddSubview[78991:832644] subView 【<UIView: 0x7f80a9c09590; frame = (0 0; 100 100); layer = <CALayer: 0x600003ff0a40>>】

根据打印结果可验证,CustomView始终只存在一个子视图(testView)。

新旧父视图一致,我们可以假设苹果做了如下处理:

1、在旧父视图中移除子视图,再重新将子视图添加到父视图上

2、判断新旧父视图是否一致,若一致,不做任何操作。

因为无法看到addSubview的源码,猜测可能会有这两种情况,个人更偏向第二种处理。(可重写子视图layoutSubviews方法,因为addSubviews会调用layoutSubviews方法,我们可以调试layoutSubviews的调用次数,测试后可验证addSubviews做了上述二的处理)

结论:若父视图重复添加同一子视图,并不会产生多层级情况。因为此例中testView是以懒加载的形式创建,所以self每次添加的均为同一个View,但如果在createUI中以UIView *testView = [UIView alloc] initWithFrame的形式创建,那就会创建出多层级的View。

总结:自定义View的子视图最好以懒加载形式创建,可避免因其他书写不当导致的异常

三、关于layoutSubviews

关于这一点,主要想聊一聊layoutSubviews的调用时机

1、setNeedsLayout\ layoutIfNeeded

2、addSubview

3、View的大小发生变化,未变不调用

4、UIScrollView滑动

5、旋转Screen会触发父UIView上的layoutSubviews事件

因此对于layoutSubviews的使用我们需要注意以下几点:

1、自定义视图的init方法并不会调用layoutSubviews

2、苹果声明不要直接调用layoutSubviews方法,如果需要更新,应该调用setNeedsLayout方法,视图会在下一次绘制后更新。如果需要立即更新视图,需要执行layoutIfNeeded方法

3、因为layoutSubviews调用比较频繁,因此若无特殊需求(文档所述为执行精确的子视图布局时可使用),不用重写layoutSubviews方法。

四、关于frame与bounds

众所周知,在iOS UI控件中有两个关于位置大小的非常重要的属性,frame与bounds

UI控件的frame意为相对于该控件父视图的位置,bounds意为相对于控件本身的位置。 frame、bounds均为结构体CGRect,由CGPoint与CGSize组成,我们可以通过 frame.origin/bounds.origin 与frame.size/bounds.size来进行返回控件左上角位置与大小。

通常给View添加动画,可以直接操作Frame或者得到Layer设置隐式动画。

那如果我们直接操作View的bounds会有什么情况出现呢?

有如下例子,有三个View,分别为RedView、BlueView、GreenView,RedView添加在当前视图控制器上,BlueView为RedView的子视图,GreenView为BlueView的子视图,坐标分别为(10,10,200,200)、(10,10,150,150)、(10,10,100,100),其坐标位置如下图所示:

若修改BlueView的bounds为(0,10,150,150),那么会有什么情况出现呢?

可能我们通常移动View不会通过bounds而是frame,并且也知道bounds是相对于自身的坐标,修改其origin不会对其本身产生什么影响,但这就大错特错了,我们来看此情况的结果,三个View的展示情况变成了下图所示:

BlueView位置并没有什么变化,GreenView却因为BlueView的修改,其位置上移了10坐标点!

我们来看下原因,因为调整里BlueView的bounds,导致BlueView相对于自己的坐标上移了10坐标点,GreenView相对于其父视图的位置也同样上移了10坐标点。对于GreenView,他的父视图BlueView的左上角已经不是(0,0),而是(0,10),因此会有上图的结果。

总结

在CustomView中尽量使用frame来做某些操作,不出于特殊需求,不要修改bounds的origin属性,会造成难以预期的Bug。(不会影响当前视图,但是会间接影响其子视图)

以上就是IOS开发自定义view方法规范示例的详细内容,更多关于IOS开发自定义view的资料请关注我们其它相关文章!

(0)

相关推荐

  • ios UITableView 自定义右滑删除的实现代码

    公司有个奇葩需求.删除按钮带点圆角 不止如此,还有cell之间有间隔,cell圆角,cell左右有间隔.如下图!!!!! 内心奔溃的我想了想了很多方法.(获取系统自带按钮改圆角也试过,自定义手势也试过)最后决定全部自定义.个人感觉这样最合适.下面是效果图 今天有时间,稍微说下实现方式: 这个项目工程只是提供一种思路,应对场景是 需要自定义左滑删除按钮的样式. 因为项目本身并不是修改系统的左滑删除,而是自定义实现,所以任何样式都算使用. 下面先说下项目的结构类型 最底下自然是uitableview

  • IOS自定义UIView

    IOS中一般会用到几种方式自定义UIView 1.继承之UIView的存代码的自定义View 2.使用xib和代码一起使用的自定义View 3.存xib的自定义View(不需要业务处理的那种) 本文主要就介绍下存代码的自定义UIView和能够在storeboard中实时显示效果的自定义UIView 先上效果图 上面为设计界面,能够直接显示一个View的圆角与边框线 上面那个圆形饼图是用纯代码自定义的 1.实现在storeboard中实时显示效果的自定义UIView  1.创建MyView.h 继

  • iOS自定义View实现卡片滑动

    本文实例为大家分享了iOS自定义View实现卡片滑动效果的具体代码,供大家参考,具体内容如下 说明 控件基于UIView封装完成,采用UIPanGestureRecognizer监听自身的触摸事件,以此处理各种滑动动画操作. 内容之间可以循环切换,采用类似tableView加载机制,达到复用效果 效果 代码实现 #import <UIKit/UIKit.h> @class SMSwipeView; @protocol SMSwipeDelegate <NSObject> @requ

  • IOS 开发之PickerView自定义视图的实例详解

    IOS 开发之PickerView自定义视图的实例详解 例如选择国家,左边是名称右边是国家,不应该使用两列,而是自定义PickerView的一列,可以通过xib来实现. 注意,虽然PickerView也是一列,但是数据源方法是@required,所以必须实现. 因此,核心思想就是一列,自定义PickerView的行视图. 使用viewForRow方法可以设定行视图. 这样的视图可以通过xib和它的控制器进行封装: Xib的控制器继承自UIView类即可. 控制器维护一个用于设置数据的模型对象fl

  • iOS中UITableView Cell实现自定义单选功能

    今天分享下cell的单选,自定义的,不是下图这种网上找到的打对勾的,我搜了好久,基本上都是打对勾的文章,就决定自己写一篇.基本上自己的app都会有一个风格吧,咱也不能一直用打对勾的方式去做(看起来是不是很low). 我们要实现的是下面的这种形式.瞬间好看了很多,高大上了很多是吧. 具体我来给大家介绍一下.我这种方法有可能不是很好,有大神来,欢迎多多交流. 首先在你自定义的cell里面加入一个UIImageView,因为你肯定要有选择和未选择两张图片的吧,所以这个UIImageView来切换图片.

  • iOS自定义可展示、交互的scrollView滚动条

    上一篇简述了封装上拉.下拉刷新控件,本篇在此基础上添加了一个自定义的scrollView滚动条,可展示.交互,首先看一下效果图: 简单阐述一下实现逻辑:自定义滚动条视图继承UIView,添加滚动条滑动事件.其他区域点击事件,通过代理方法与列表关联.在列表刷新完成及scrollView代理方法中更新滚动条. 简单说一下计算逻辑,如上图(原谅博主的图)所示,其中b.c.d是已知的.首先计算滚动条的高度a,理想情况下它与整个滚动区域b的比值应该等于scrollView的展示区域b与scrollView

  • IOS开发自定义view方法规范示例

    目录 前言 一.关于自定义View的初始化方法 二.关于addSubview 三.关于layoutSubviews 四.关于frame与bounds 总结 前言 对于接触业务开发的童鞋,自定义View的开发是进行最频繁的工作了.但发现一些童鞋还是没有以一个好的规范甚至以一种错误的方式来搭建UI控件.由此,本文将以以下目录来进行讲叙,详细描述关于自定义View的一些书写注意事项. 关于自定义View的初始化方法 关于addSubview 关于layoutSubviews 关于frame与bound

  • iOS开发探索多线程GCD任务示例详解

    目录 引言 同步任务 死锁 异步任务 总结 引言 在上一篇文章中,我们探寻了队列是怎么创建的,串行队列和并发队列之间的区别,接下来我们在探寻一下GCD的另一个核心 - 任务 同步任务 void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block); 我们先通过lldb查看其堆栈信息,分别查看其正常运行和死锁状态的信息 我们再通过源码查询其实现 #define _dispatch_Block_

  • iOS开发探索多线程GCD队列示例详解

    目录 引言 进程与线程 1.进程的定义 2.线程的定义 3. 进程和线程的关系 4. 多线程 5. 时间片 6. 线程池 GCD 1.任务 2.队列 3.死锁 总结 引言 在iOS开发过程中,绕不开网络请求.下载图片之类的耗时操作,这些操作放在主线程中处理会造成卡顿现象,所以我们都是放在子线程进行处理,处理完成后再返回到主线程进行展示. 多线程贯穿了我们整个的开发过程,iOS的多线程操作有NSThread.GCD.NSOperation,其中我们最常用的就是GCD. 进程与线程 在了解GCD之前

  • Python开发自定义Web框架的示例详解

    目录 开发自定义Web框架 1.开发Web服务器主体程序 2.开发Web框架主体程序 3.使用模板来展示响应内容 4.开发框架的路由列表功能 5.采用装饰器的方式添加路由 6.电影列表页面的开发案例 开发自定义Web框架 接收web服务器的动态资源请求,给web服务器提供处理动态资源请求的服务.根据请求资源路径的后缀名进行判断: 如果请求资源路径的后缀名是.html则是动态资源请求, 让web框架程序进行处理. 否则是静态资源请求,让web服务器程序进行处理. 1.开发Web服务器主体程序 1.

  • IOS 开发自定义条形ProgressView的实例

    IOS 自定义进度条 ProgressView,好的进度条,让人赏心悦目,在等待的时候不是那么烦躁,也算是增加用户体验吧! 进度条在iOS开发中很常见的,我在项目开发中也写过好多进度条,有好多种类的,条形,圆形等,今天给大家总结一种条形的开发进度条. 简单思路: 1.自定义进度条先继承UIView 建立一个CustomBarProgressView  2.在.H文件中外漏的方法<开始的方法><初始化的方法>  3.在.M文件中 利用定时器改变位置 实现进度条 #效果图 #部分代码

  • iOS开发-自定义相机实例(仿微信)

    网上有很多自定义相机的例子,这里只是我临时写的一个小demo,仅供参考: 用到了下面几个库: #import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/AssetsLibrary.h> 在使用的时候需要在Info.plist中把相关权限写进去: Privacy - Microphone Usage Description Privacy - Photo Library Usage Description Privacy

  • iOS开发中UITabBarController的使用示例

    首先我们看一下它的view层级图: 复制代码 代码如下: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  {      self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];      // Override point fo

  • ios开发中的容错处理示例详解

    前言 后台服务器返回给客户端的值有时会是null,有时会是"<null>",直接赋值并进行后续操作有时会导致崩溃. 之前的处理方式都是尽量让后台服务器返回数据时不返回null或者是"<null>",但是他们还是时不时返回这些数据,所以app时不时就会出现闪退现象.一出现这种问题,调试后发现是他们返回null或者是"null"的数据类型,因为是线上的问题,所以让他们直接在后台将出现问题的字段进行处理就好了.久而久之,发现这种

  • IOS 开发之触摸事件详细介绍

    IOS 触摸事件 iOS中的事件可以分为3大类型: 触摸事件 加速计事件 远程控制事件 响应者对象 在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件.我们称之为"响应者对象". UIApplication.UIViewController.UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件. UIResponder内部提供了以下方法来处理事件 触摸事件(对应Android的action_down.acti

  • iOS开发之自定义UITextField的方法

    UITextField是IOS开发中用户交互中重要的一个控件,常被用来做账号密码框,输入信息框等. 观察效果图 UITextField有以下几种特点: 1.默认占位文字是灰色的 2.当光标点上去时,占位文字变为白色 3.光标是白色的 接下来我们通过不同的方法来解决问题 一.将xib中的UITextField与代码关联 通过NSAttributeString方法来更改占位文字的属性 (void)viewDidLoad { [super viewDidLoad]; // Do any additio

随机推荐