【引用】:http://www.jianshu.com/p/c5fc8c6b967a

【View层】IOS纯代码绘制界面(一)

字数2303 阅读385 评论2 喜欢16

IOS开发中界面绘制占据了绝大部分的工作量,当前可以使用StoryBoard,xib以及代码三种方式定制所需要的界面。在这三种方式中代码是最灵活,最具有扩展性的,而特别是多人合作的大项目中StoryBoard,xib不利于代码合并。
接下来对之前项目的的界面开发做一些总结。

  • UIViewController和重要子类

    • UINavigationController
  • UIView和重要子类
    • UIControl
    • UIScrollView
  • 各种常用控件的注意点(后续介绍)
  • 应用约束Masonry库(后续介绍)
  • 常用的Code Snippet(后续介绍)

UIViewController和重要子类

UINavigationController
打交道最多的类无疑是这两个,一般我们采用UINavigationController(导航控制器,navi)作为UIViewController(vc)的容器,最常用的方法是

//用指定的vc创建,该vc位于栈底。
-(instancetype)initWithRootViewController:(UIViewController *)rootViewController; //向栈里push一个vc,位于栈顶
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; //从栈里pop一个vc。
-(nullable UIViewController *)popViewControllerAnimated:(BOOL)animated;

一般app中只需要用到一个navi,在AppDelegate中完成初始化即可,后续的界面迁移都是push和pop。值得注意的是使用了navi之后,每个界面会产生一个navigationBar,即位于界面顶部的导航条。导航条的作用是显示当前的界面title和返回按钮,并支持自己添加需要的辅助功能。在任何vc中可以通过

self.navigationController.navigationBar或者self.navigationItem

访问这个组件,如设置背景色,添加按钮等:

[self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:19], NSForegroundColorAttributeName : someColor}];
[self.navigationController.navigationBar setBackgroundColor:[UIColor whiteColor]];
UIBarButtonItem * activityDetail = [[UIBarButtonItem alloc] initWithTitle:@“帮助” style:UIBarButtonItemStylePlain target:self action:@selector(helpButtonClicked)];
self.navigationItem.rightBarButtonItem = activityDetail;
UIViewController

作为视图控制器基类,我们一般在里面添加view代码,显示逻辑,包括按钮事件、代理方法(比如tableView的代理等)。不注意的话vc会变得很大,如果行数过多则不易维护,所以说复杂的页面应该做架构上的区分,如MVVM模式将view,viewModel抽离出来。这里暂不细讲。
vc最重要的方法包括:

生命周期相关的方法

//重写该方法意味着不加载外部的xib文件,自己绘制界面
-(void)loadView;
-(void)viewDidLoad;
-(void)viewWillAppear:(BOOL)animated;
-(void)viewDidAppear:(BOOL)animated;

以上方法在view加载过程中依次调用。一般在loadView或者viewDidLoad中添加自定义的view,但不能添加过于耗时的操作,以避免界面加载被阻塞导致的卡顿。一般把如网络操作,数据库操作加载viewDidAppear中,界面加载可见之后执行。

UIView和重要子类

UIView

先总结UIView的通用方法和属性,然后介绍常用view控件的使用经验。
初始化方法,以一个frame作为参数。

  • (instancetype)initWithFrame:(CGRect)frame

基本

  • userInteractionEnabled
    默认YES,代表是否具有用户可操作性,设为NO时任何手势都不生效。在视图重叠的时候,用户的触摸会到达第一个开启了userInteractionEnabled=YES的视图。这个特性有两种用法:穿透和拦截,前者可以制作透明的不可交互背景,后者可以做比如加载中这种遮罩。
  • layer
    UIView内部实现,类型是CALayer,并封装了许多未暴露到UIView中的内容,如cornerRadius属性实现圆角只能调用view.layer.cornerRadius。
  • tag
    结合viewWithTag使用,类似于android中的findViewById,类似于JavaScript中的getElementById的用法。但不是全局使用,而是在一个view的子视图中查找目标tag。典型场景如两个控件绑定了一个vc作为delegate,实现的协议中可以通过tag来判断是来自哪个控件的事件。

位置与空间相关:

  • frame
  • bounds

每个UIView有frame 和bounds两个属性,分别代表相对父容器的矩形和相对本容器的矩形(有点难理解)

struct CGRect {
CGPoint origin;
CGSize size;
};

bound一般用来修改view的相对位置,类似于css中的相对布局,frame类似绝对布局。如图

Paste_Image.png
  • center
    中心点CGPoint
  • transform
    视图的仿射变换,默认是CGAffineTransformIdentity,解释为原始变换,意思就是维持原样。通过修改这个值可以改变view的外观,尤其在动画中常用。
  • contentScaleFactor
    缩放比例CGFloat
  • -sizeThatFits:
    方法,返回适合其中子类的大小。

view的层次

  • subviews
    返回所有的子视图
  • superview
    返回父视图
  • window
    返回view的window对象
  • -(void)removeFromSuperview;
    把自己从父view中删掉
  • -(void)addSubview:(UIView *)view;
    添加子view
  • -(void)bringSubviewToFront:(UIView *)view;
    把子view拉到最前(重叠的情况下,默认按照添加顺序来一层层盖住之前的view)
  • -(void)sendSubviewToBack:(UIView *)view;
    把子view放到最后
  • -(void)layoutSubviews;
    override point. called by layoutIfNeeded automatically. As of iOS 6.0, when constraints-based layout is used。该方法的调用时机:
    addSubview会触发layoutSubviews
    设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
    滚动一个UIScrollView会触发layoutSubviews
    旋转Screen会触发父UIView上的layoutSubviews事件 -改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

view的绘制

  • (void)drawRect:(CGRect)rect;
    由setNeedsDisplay来触发调用,可以重写该方法来实现一个自定义view,例如在里面执行
- (void)drawRect:(CGRect)rect {
// Drawing code.
//获得处理的上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//设置线条样式
CGContextSetLineCap(context, kCGLineCapSquare);
//设置线条粗细宽度
CGContextSetLineWidth(context, 1.0); //设置颜色
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
//开始一个起始路径
CGContextBeginPath(context);
//起始点设置为(0,0):注意这是上下文对应区域中的相对坐标,
CGContextMoveToPoint(context, 0, 0);
//设置下一个坐标点
CGContextAddLineToPoint(context, 100, 100);
//设置下一个坐标点
CGContextAddLineToPoint(context, 0, 150);
//设置下一个坐标点
CGContextAddLineToPoint(context, 50, 180);
//连接上面定义的坐标点
CGContextStrokePath(context);
}

调用时机drawRect是在Controller.loadView,viewDidLoad 两方法之后掉用的.所以不用担心在控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View draw的时候需要用到某些变量值).

  • clipsToBounds
    默认NO,代表子元素显示时可以超出该view的边界。
  • backgroundColor
    背景颜色
  • alpha
    透明度,默认为1.0代表不透明,注意当alpha为0时等同于hidden,控件无法接收到点击事件。注意该值会影响子视图的绘制,但子视图的alpha不受影响,子视图绘制时的alpha实际效果等于包含链上的所有alpha之乘积。
  • opaque
    不透明,默认为YES
  • hidden
    隐藏,默认为NO
  • contentMode
    参考UIViewContentMode,有多重展示的模式,如图:

Paste_Image.png

动画相关

- +(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^ )(void))animations completion:(void (^ __nullable)(BOOL finished))completion

方法接受几个参数:duration, delay, options, animations, completion。duration代表执行多久delay代表延迟多少时间开始执行,option参考UIViewAnimationOptions(篇幅过大此处略过),animation和completion各为一个block,代表执行的动画和完成后执行的代码。一段示例代码(慢慢放大后迅速缩小的提示效果):

Paste_Image.png

其他方法和该方法类似,实际使用时根据实际情况决定。

手势相关
添加和删除手势,不过多介绍。

  • -(void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
  • -(void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;
UIControl

子类包括:

Paste_Image.png

其中UIButton和UITextField,UISwitch都是平时十分常用的控件,作为他们的基类有必要介绍一下。

重要的属性与方法:

  • enabled
    是否激活,默认YES,设为NO在视觉上会变为灰色
  • selected
    是否选中,默认NO,参考UIControlState
  • highlighted
    是否highlighted,默认NO,参考UIControlState
  • state
    UIControlState类型

UIControl 基本的 State 变化过程如下
1.什么都没干的时候:Normal
2.当你的手指按下去,还没放的时候:Highlighted
3.当手指放开的时候:如果这个 UIControl 有 Selected 状态的话,就会变成: Selected
再重复上述过程一次,就会从 Selected->Highlighted-> Normal
但是普通的 UIButton 这个 UIControl 的 subclass,是没有 Selected 状态的,它就只有 Normal 和 Highlighted,只会在这两个状态间切换。(正常情况下,如果你设置了 disable 的话,还会变到 Disabled)

按钮事件

- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;

普通的按钮通过该方法绑定事件即可,其中sel是一个方法指针,UIControlEvents定义了可发送事件的几种类型。按钮点击一般使用UIControlEventTouchUpInside。代表在控件范围内按下去并放开。其余的属性可参考UIControlEvents。

自定义事件实现

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event;

通过以上三个方法可以实现自定义的控件,比如要自己实现滑块效果,先调用begin,然后在continue中利用touch.locationInView的值来改变滑块代表的数值,在end中完成网络,数据库等操作。

【View层】界面绘制的更多相关文章

  1. android View层的绘制流程

    还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得 ...

  2. iOS应用架构谈(二):View层的组织和调用方案(上)

    OS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.上篇主要讲View层的代码结构. ...

  3. iOS应用架构谈 view层的组织和调用方案

    当我们开始设计View层的架构时,往往是这个App还没有开始开发,或者这个App已经发过几个版本了,然后此时需要做非常彻底的重构. 一般也就是这两种时机会去做View层架构,基于这个时机的特殊性,我们 ...

  4. (转)iOS应用架构谈 view层的组织和调用方案

    前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二 ...

  5. Android显示框架:自定义View实践之绘制篇

    文章目录 一 View 二 Paint 2.1 颜色处理 2.2 文字处理 2.3 特殊处理 三 Canvas 3.1 界面绘制 3.2 范围裁切 3.3 集合变换 四 Path 4.1 添加图形 4 ...

  6. iOS应用架构谈-part2 view层的组织和调用方案

    前言 <iOS应用架构谈 开篇>出来之后,很多人来催我赶紧出第二篇.这一篇文章出得相当艰难,因为公司里的破事儿特别多,我自己又有点私事儿,以至于能用来写博客的时间不够充分. 现在好啦,第二 ...

  7. iOS应用架构谈(二):View层的组织和调用方案(中)

    iOS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.中篇主要讨论MVC.MVCS. ...

  8. ZRender源码分析4:Painter(View层)-中

    回顾 上一篇说到:ZRender源码分析3:Painter(View层)-上,接上篇,开始Shape对象 总体理解 先回到上次的Painter的render方法 /** * 首次绘图,创建各种dom和 ...

  9. pureMVC简单示例及其原理讲解三(View层)

    本篇说的是View层,即视图层,在本示例中包括两个部分:MXML文件,即可视控件:Mediator. 可视控件 可视控件由UserForm.mxml(图1)和UserList.mxml(图2)两个文件 ...

随机推荐

  1. HDU 3552 I can do it!

    脑洞题.http://blog.csdn.net/dgq8211/article/details/7748078 #include<cstdio> #include<cstring& ...

  2. Win7与Ubuntu双系统时卸载Ubuntu的方法

    Win7与Ubuntu双系统时卸载Ubuntu的方法 [日期:2010-03-26] 来源:Ubuntu社区  作者:Ubuntu编辑 [字体:大 中 小]       1. 下载MBRFix工具,放 ...

  3. 空间表SpaceList

    比如在建一个成绩管理系统,这时候定义的名字一般都是char szName[20],这样比较浪费,其实不只是定义名字,定义好多变量都这样,并没有体现动态. 此处出现空间表(SpaceList),通过指针 ...

  4. hdu_5724_Chess(组合博弈)

    题目链接:hdu_5724_Chess 题意: 给你一个n行20列的棋盘,棋盘里面有些棋子,每个棋子每次只能往右走一步,如果右边有棋子,可以跳过去,前提是最右边有格子,如果当前选手走到没有棋子可以走了 ...

  5. 【摘自网络】dll库和lib库有什么区别

    简单地讲:第一:.DLL是动态链接库,而.LIB是静态链接库dll是个编译好的程序,调用时可以直接调用其中的函数,不参加工程的编译. 而lib应该说是一个程序集, 只是把一些相应的函数总结在一起, 如 ...

  6. Android 手机应用开发经验 之 通过Socket(TCP/IP)与PC通讯

    Android 是一个开源的手机操作系统平台,已经被非常多的开发者视作未来最有潜力的智能手机操作系统.而且,在很短的时间内就在Android Market上出现大量的第三方应用程序,供用户下载与使用, ...

  7. HDU 1074 Doing Homework(状态压缩)

    之前做过一个题,是在学贪心的时候做的,所以这个题就想当然的跑偏了,当看到N是<=16 的时候,状态压缩就理所当然了 #include<iostream> #include<cs ...

  8. php url 伪静态

    手册上说:   'PATH_INFO'    包含由客户端提供的.跟在真实脚本名称之后并且在查询语句(query string)之前的路径信息,如果存在的话.例如,如果当前脚本是通过 URL http ...

  9. 关于WebAPI安全认证的问题

    之前项目遇到了一个对外提供API的问题,有些粗浅的想法,抽空记录下. 以下所讨论的都是要解决:谁有资格调用这个API的问题(谁有权限进行这个操作的问题) 为了简化思路,就拿一个查看Java班级的学生举 ...

  10. 合并BIN文件的两种方法(转)

    源:http://blog.chinaunix.net/uid-20745340-id-1878803.html 合并BIN文件的两种方法 在单片机的开发过程中,经常需要将两个单独的BIN文件合并成一 ...