六.用户界面

六.用户界面

主要讨论如火如何最小化更新UI所需的时间。

6.1视图控制器

视图控制器的生命周期: 输入图片说明php

建立试图控制器时须要遵循的基本最佳实践:html

  • 保持视图控制器轻量。在MVC结构应用中,控制器只是纽带,而不是存放全部业务逻辑的地方。它甚至不属于模型。业务逻辑应该属于服务层或业务逻辑组件。
  • 不要在视图控制器中编写动画逻辑。动画能够在独立的动画类中实现,该类接受视图做为参数传入,这些视图就是用来运行动画的视图。而后视图控制器会将动画添加至视图或转场效果上。
  • 使用数据源和委托协议,将代码按照数据检索,数据更新和其余业务逻辑进行分离。视图控制器只能用来选择正确的视图,并将它们链接到供应源。
  • 视图控制器响应来自视图的事件,如按钮点击事件或单元格的选择事件,而后将它们链接至数据接收器。
  • 视图控制器响应来自操做系统的UI相关事件,如方向变化和低内存警告。这可能会触发视图的从新布局。
  • 不要编写自定义的init代码。若是视图控制器被从新切换至XIB或故事板,那init方法永远都不会被调用。
  • 不要在视图控制器中使用代码手工布局UI,也不要在视图控制器中实现所有的UI,视图建立和视图布局逻辑等操做。使用nib或者故事板。
    手工布局代码不会持续好久,由于应用在不断增加,而且设计也在改变。在从新设计方面,使用interface Builder比根据像素坐标来手动编写代码更快。 若是某一设计被分在独立的nib和故事板,能够比较灵活运行A/B测试https://www.zhihu.com/question/20045543,由于在不一样的约束之间很容易选择最终须要的。并且自定义代码不容易适配设备。
  • 建立一个实现公共设置的基类控制器。
  • 在各视图控制器之间,使用category建立可复用的代码。这样就不会被限制只能使用预约义的基类,同时还能获得复用带来的好处。

6.1.1视图加载

视图初始化时会涉及两个方法--loadview和viewDidLoad. 当添加一个新的视图控制器时,经过Xcode生成的模板代码只有viewDidLoad方法。当视图控制器的view被请求时,loadview方法会被调用,但由于它还未被建立,因此会是nil。ios

三种加载视图方式:git

  • 从nibs(xibs)
  • 使用故事板
  • 自定义代码建立UI

若是经过覆写loadview方法建立了自定义UIgithub

  • 将view属性设置大片视图层级的跟上。
  • 确保视图正被其余的视图控制器所共享。
  • 不要调用[super loadview]

iOS用户界面:Storyboards vs. NIBs vs. Custom Code http://www.jianshu.com/p/10dd75d34a20web

6.1.2视图层级

视图结构和渲染包括如下步骤: (1)构造子视图。 (2)计算并提供约束。 (3)为子视图递归地执行步骤1和步骤2. (4)递归渲染 viewDidAppear:方法会由于过分动画的缘由在约300毫秒后被调用。swift

6.1.3视图可见性

视图控制器提供了4个生命周期方法,以接受有关视图可视性的通知。数组

  • viewWillAppear:当视图层级已经准备好,且视图即将被放入视图窗口时,此方法会被调用。 在这个时刻,过渡动画还未开始,视图对终端用户也是不可见的。不要启动任何视图动画,由于没有任何做用。缓存

  • viewDidAppear: 当视图在视图窗口展现出来,且过渡动画完成后,此方法会被调用。 启动或恢复任何想要呈现给用户的视图动画。服务器

  • viewWithDisappear:该方法表示视图将要从屏幕上隐藏起来。这多是由于其余视图控制器想要接管屏幕,或该视图控制器将要出栈。此方法被调用时,没有办法可以直接判断这是由当前视图控制器要出栈仍是其余视图控制器入栈致使的。区分的惟一方法是扫描当前视图控制器navigationController的viewController属性

NSInteger index = [self.navigationController.viewControllers indexOfObject:self];
	if(index == NSNotFound){
		//即将出栈,销毁
	}else{
		//只是保存状态,暂停
	}
  • viewDidDisappear:当上一个/下一个视图控制器的过渡动画完成时,此方法会被调用。

高效使用生命周期事件的最佳实践:

  • 不要重写loadview。
  • 将viewDidLoad做为最后的检查点,查看来自数据源的数据是否可用。若是可用,则更新UI。
  • 若是每次都须要展现最新的信息,那么就用viewwillappear:更新UI元素。
  • viewDidAppear:中开始动画。
  • viewWillDisappear:来暂停或中止动画。
  • viewDidDisappear:销毁内存中的复杂数据结构。也能够在这里注销与视图控制器绑定的数据源通知,以及与动画,数据源,UI更新有关的应用事件通知中心。

6.2视图

基本规则:

  • 尽可能减小在主线程中所作的工做。
  • 避免较大的xibs或故事板。
  • 避免在视图层次结构中多层嵌套。在层次结构的任何位置添加视图时,它的祖先树节点会执行值为YES的setNeedsLayout:方法,当事件队列正在执行时,该设置会触发layoutSubviews:。这个调用代价较大,由于视图必须根据约束从新计算子视图的位置。
  • 尽量延迟加载视图并进行重用。
  • 对于复杂的UI而言,最好使用自定义绘图。这样只会触发一个视图进行绘制,而不是多个子视图,同时也避免了调用代价较高的layoutsubviews和drawRect:方法。

6.2.1UILabel

(1)使用字体、字体类型以及要被渲染的文本时,计算须要的像素数目。这是一个消耗较大的过程,应尽量少地去作。 (2)检查要被渲染的宽度。 (3)检查numberOfLines,计算将要展现的行数。 (4)sizeToFit是否被调用?若是是,计算高度。 (5)若是sizeToFit没有被调用,检查当前的内容可否在给定的高度下展现出来。 (6)若是frame不够,使用lineBreakMode肯定隐藏或截断的位置。 (7)使用字典、类型及颜色来渲染最终展现的文本。

6.2.2UIButton

渲染按钮的方式:

选项 优势 缺点
自定义文本 最简单的方式,可直接使用 一般是比较呆板,毫无装饰的按钮
全尺寸资源 可自定义的背景 无需代码便可实现 可实现A/B测试—图像可 在运行试验时下载 图片打包在应用中,致使包变大
可变大小资源 可自定义的背景 无需代码便可实现 <div>可实现A/B测试—图像可</div> <div>在运行试验时下载</div><div>包大小的增量相对较小</div> 资源的任何更改可能都须要从新计算 重置UIEdgeInsets值
使用CALayer和 贝塞尔路径定义绘制 彻底是自定义绘图 任何格式的更改或升级均可能须要更新 应用

6.2.3UIImageView

iOS仍旧不支持GIF动画,只能建立animationImages的一个数组来存放能够生成动画的图片。或使用自定义编码和第三方库。 自定义编码:http://www.imagemagick.org/script/index.php 第三方库:https://github.com/mattt/AnimatedGIFImageSerialization

使用UIimage和UIimageview的最佳实践:

  • 对于已知的图像,使用imageNamed:方法加载图片。它能够确保内容只被加载至内存一次,还能够确保多个UIImage对象间改变用途。
  • 在使用imageNamed:方法加载图片时,使用资源包。若是应用有一堆图标,且每一个图标都较小时,这种方法极其有用。能够随意建立相关图像的多个目录。若是想加载一个只使用一次的大图像,最好谨慎思考一下,考虑使用imageWithContentsOfFile:代替资源目录和imageNamed:方法,由于资源目录缓存了这些图片。
  • 对于其余图像,使用高性能的图像缓存库。AFNetWorking和SDWebImage。当使用内存中的图片时,确保正确配置了内存的使用参数。不要使用硬编码。让它可以自适应--使用合理的RAM百分比能够较好的进行配置。
  • 载入的图像与即将渲染的UIImageView大小相同。由于调整图像大小是一个耗费较大的操做。
  • 不管使用何种技术加载图像,在非主线程中执行,最好在一个专用队列中执行。尤为要在非主线程中解压JPG/PNG图像。
  • 确认是否真的须要使用图像,最好使用直接绘制的自定义视图,而不是多个图像。

6.2.4UITableView

输入图片说明

最佳实践:

  • 在数据源的cellForRowAtIndexPath:方法里,使用dequeueReusableCellWithIdentifier:进行单元格的重用。
  • 尽量避免动态高度的单元格。若是想要使用可变高度的单元格,最好选择较高的单元格,由于这只须要为较少的单元格计算高度,从而减小了计算量。
  • 若是你真的须要动态高度的单元格,那么定义一个规则来标记单元格为脏的。若是某个单元格是脏的,计算它的高度并缓存。在委托的heightForRowAtIndexPath:回调中继续返回缓存的高度,直到单元格再也不被标记为脏。
  • 当用自定义视图重用单元格时,要避免经过调用layoutIfNeeded每次都对其进行布局。
  • 避免透明的单元格子视图。
  • 在快速滚动时,考虑使用界面外壳。当用户快速滚动列表视图时,虽然使用了全部的优化,但视图的重用和渲染仍然须要超过16毫秒,还有可能出现偶发的丢帖现象,从而致使不流畅的体验。在这种状况下,使用一个界面外壳是一个较好的选择,外壳能够被预约义,它的惟一目的就是告诉终端用户这些部分即将展现一些数据。当滚动速度下降,并低于阈值时,刷新最终的视图并填充数据。可使用与列表视图相关联的panGestureRecognizer属性获取速度值。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
	CGPoint velocity = [self.tableView.panGestureRecognizer velocityInView:self.view];
	self.velocity = velocity;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	
if (fabs(self.velocity.y) > 2000){
		//返回界面外壳
	}else{
		//返回真正的单元格
	}
}
  • 避免渐变、图像缩放以及任何屏幕外的绘制。

6.2.5UIWebView(iOS7),WKWebView(iOS8),SFSafariViewController(iOS9)

UIWebView是用于渲染未知或动态内容的最多见视图。一般状况下,会将web视图指向一些内嵌的HTML或web URL. 经常使用场景:

  • 除了原生UI渲染登录表单。若是想用CAPTCHA(验证码https://zh.wikipedia.org/wiki/验证码)筛选刷屏的机器人,就须要为全部的格式提供支持,并将其打包至应用中,或将用户指向一个网页登录URL,让服务器生成任何须要的复杂UI。
  • 在任何应用中显示隐私政策或使用条款。由于这些会随着时间变化,而且须要大量的格式化,使用原生视图不是较好的选择。
  • 新闻或文章阅读器,由于大部分的文章都是Web建立的,几乎都是HTML。
  • 邮件应用。例如,初始邮件是HTML形式,当呈现消息或跟帖,以及撰写回复时。

须要展现较小的富文本,使用UIlabel的NSAttributedString.

最佳实践:

  • UIWebView可能比较笨重且迟钝,因此尽可能复用web view。同时,UIWebView也因内存泄露而知名。不管什么时候想向用户展现新的URL,先将内容重置为空的HTML。这样就能确保web view不会将以前的内容展现给用户。想要实现这一功能,在loadRequest:方法后调用loadHTMLString:baseURL:便可。
  • 实现webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 方法。要留意URL scheme。若是是http或https之外的东西,须要注意:应用应该知道如何处理这种状况,或警告用户该网站正试图脱离应用。
  • 能够经过stringByEvaluatingJavaScriptFromString:方法建立一个桥来链接应用和JavaScript(iOS8后WKWebView的evaluateJavaScript:completionHandler: 代替),从而字当前已经加载的web页面执行JavaScript。
  • 实现委托的webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error方法,保持对全部可能出现的错误进行追踪。若是域名与NSURLErrorDomain相等,那么NSError对象有不一样的意义。
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
	if ([NSURLErrorDomain isEqualToString:error.domain]){
		switch (error.code) {
			case NSURLErrorBadURL:
    //处理错误的URL
				break;
			case NSURLErrorTimedOut:
				//处理超时
				break;
			case NSURLErrorUnknown:
				//未知
				break;
			//其余。。。
			default:
    break;
		}
	}
}
  • UIWebView不会通知任何的HTTP协议错误,例如响应是404或500错误。因此须要出发两次调用,第一次使用自定义的NSURLConnection调用,而后是经过web view的调用。
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if (self.shouldValidate){
	[NSURLConnection connectionWithRequest:request delegate:self];
		return NO;
	}
	return YES;
}

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
	NSInteger status = [(NSHTTPURLResponse*)response statusCode];
	if (status >= 400){
		//展现警报或隐藏web view ---不要展现错误的网页
	}else{
		self.shouldValidate = YES;
		[self.webView loadRequest:connection.originalRequest];
	}
}
  • 嵌入的UIWebView的容器应该提供一下元素。

    1.导航按钮。 2.重载按钮。 3.取消按钮,用于取消当前正在加载的页面。 4.用于展现标题的UILable. 5.退出web view的关闭按钮。

    6.2.6 自定义视图

输入图片说明

简单复合视图UI的基本实现可能包含如下内容: (1)UIImageView做为头像图片。 (2)用UIlabel的NSAttributedText展现用户名称。 (3)UITextView,展现主要内容,由于里面可能包含连接。 (4)通常数据展现UILable。

自定义视图则经过在drawRect:中直接绘制所有的元素。

1.复合视图 建立一个UITableViewCell的子类,勾选XIB file。而后直接在XIB排列所需元素。 针对复合视图,在动画过程当中使用视图光栅化http://swift.diagon.me/shouldRasterize/

2.直接绘制 不勾选XIB文件选项,覆盖drawRect:方法以自定义渲染元素。

优缺点比较:运行时性能和代码维护 *直接绘图的自定义视图中的运行时性能更好。(经过首次初始化时间,后续初始化,滚动后的首次初始化,滚动后的第二次初始化,重用等对比) *从维护的角度来看,代码会难以维护和发展。一旦应用稳定下来,能够比较明确的将复合UI换成直接绘图。

6.3自动布局

自动布局使用本地约束(元素彼此之间的位置关系)会比使用全局约束(相对于父视图的位置)更快。 自动布局衡量展现视图和渲染视图所需的时间,若是超过了阈值,应该考虑使用自定义代码。阈值根据具体的应用而定。

6.4尺寸类别

ppi(像素密度)不是点到像素的比例。 尺寸类别:https://isux.tencent.com/ios9-guideline-ch1.html 尺寸类须要自动布局。若是由于性能缘由不选择使用自动布局,那就不能使用尺寸类

6.5iOS8中新的交互特性

6.5.1交互式通知

交互式通知,容许用户提供一个针对输入的快速响应。 交互式通知的可能动做:

  • 邮件:回复,标记为垃圾。
  • 信息:提醒,回复。
  • 在社交应用中评论信息:回复评论,点赞评论。
  • 任务和提醒:稍后,标记完成。

6.5.2应用扩展

iOS8可用的应用扩展:

  • 今日:通知中心的“今日”,帮助用户快速更新或完成某一任务。
  • 自定义键盘
  • 分享:容许用户更加无缝地跨应用共享数据。
  • 行动:帮助用户查看或改变在主应用中发起的内容。
  • 照片编辑:容许用户编辑照片应用中的照片或视频。
  • 文档提供者:容许其余应用访问本身应用所管理的文件。
相关文章
相关标签/搜索