【引用】: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 - 2502 Subway

    题目链接:http://poj.org/problem?id=2502 分析: 告诉一些地铁线路,从起点到终点,中途可以步行,可以坐地铁,找一条最短的路 主要是把图建立好,然后直接dijkstra或者 ...

  2. oracle xe client 如何设置 tnsnames.ora(解决无法使用pl/sql developer的问题)

    10.2版本xe的服务器和客户端安装都很方便,由于xe的服务器只允许建立一个实例,实例名字会直接默认为xe,客户端默认安装在C:\XEClient目录下,使用sqlplus连接服务器: sqlplus ...

  3. Java 8新特性探究(九)跟OOM:Permgen说再见吧

    PermGen space简单介绍 元空间(MetaSpace)一种新的内存空间诞生 PermGen 空间的状况 Metaspace 内存分配模型 Metaspace 容量 Metaspace 垃圾回 ...

  4. Stem Cell 华人科学家

    Jianping Fu 密歇根大学机械工程系生物医学工程专业 PhD, Massachusetts Institute of Technology, 2007MS, University of Cal ...

  5. HBase的Shell命令

    1.HBase提供了一个shell的终端给用户交互 2.HBase Shell的DDL操作 (1)先进入HBase的 Shell命令行,即HBASE_HOME/bin/hbase shell …… & ...

  6. FAT32系统中长文件名的存储(转)

    FAT32的一个重要的特点是完全支持长文件名.长文件名依然是记录在目录项中的. 为了低版本的OS或程序能正确读取长文件名文件,系统自动为所有长文件名文件创建了一个对应的短文件名,使对应数据既可以用长文 ...

  7. IPoint Interface接口

    Description A Point is a zero-dimensional object that represents a specific (X, Y) location in a the ...

  8. Android] Android XML解析学习——方式比较

     [Android] Android XML解析学习——方式比较 (ZT)  分类: 嵌入式 (From:http://blog.csdn.net/ichliebephone/article/deta ...

  9. DOS日期和时间 - Robin Hu的专栏 - 博客频道 - CSDN.NET

    body { font-family: Microsoft YaHei UI,"Microsoft YaHei", Georgia,Helvetica,Arial,sans-ser ...

  10. Mac os 10.11 更新ruby

    1.装cocoapods,ruby版本忒低->开始更新ruby->开始更新gem,这是一条不归路啊同志们,各种permission denied,各种路径不存在,各种路径没有读写权限,各种 ...