如何设置XCODE的设置字节对齐齐

编译对齐方式设置 - CSDN博客
编译对齐方式设置
from: /kf/833.html
GCC 4.7+ version 字节对齐
1、位域对齐
3.7版本之后GCC都默认使用了-mms-bitfields,此选项意义为使用Microsoft的方式进行对齐操作,其对齐策略为将对所有类型相同的位域合并到一起。与之相对的是GCC对其方式,其对齐策略为将所有位域合并到一起,并不区分位域类型。
struct { &
unsigned long long c : 1; &
unsigned int a : 1; &
unsigned int b : 1; &
Mircrosoft对齐方式将合并类型相同的a、b,为之分配8个字节,而对于c,则单独分配8个字节,所以上述结构体在此对齐方式下大小为16.
GCC对齐方式将不区分类型合并所有的位域,并根据原类型中最大尺寸分配位域保存所需的字节数。譬如对于上述结构,最大尺寸为long long,所以上述结构体大小为8.
此结论可以通过为struct添加不同的GCC结构属性测试得到。
struct fields { &
& & unsigned long long c :1; &
& & unsigned int a :1; &
& & unsigned int b :1; &
//}__attribute__ ((__ms_struct__)); //用于测试Microsoft对齐方式。得到sizeof(fields)结果为16。 &
//}__attribute__ ((__gcc_struct__));//用于测试GCC对齐方式。得到sizeof(fields)结果为8。 &
从gcc与ms(Microsoft)的对齐策略可见,gcc的对齐方式相对更加节约存储空间。
2、结构对齐
先看一个结构体:
struct struct_ { &
& & char first[1]; &
& & char third[1]; &
此结构体使用默认的GCC选项编译后,既默认使用-mms-bitfields选项的情况下,将得到大小为9字节的存储空间。
这是因为对于32位操作,GCC默认对齐字节为4字节,在使用MS结构对齐策略的情况下,GCC在处理第一个变量时保持其尺寸不变,既为1字节,在处理第二个变量时同样保持其尺寸不变,既为4字节,在处理第三个也是最后一个变量时,扩展其尺寸至对齐尺寸4字节。因此最后得到的总结构大小为1+4+4=9字节。
总而言之,MS的对齐策略仅对结构中最后一个变量起作用。
如果关闭默认的MS对齐策略(通过为GCC添加-mno-ms-bitfields参数),转而使用GCC的对齐策略,上述结构的尺寸将会是多少?
测试的结果是:12。为何?这是因为GCC将对齐策略应用在了结构体中的每个变量上,对于非4字节的整数倍的char类型变量first[1]、third[2],GCC都将其扩展为4个字节,对于second,由于其类型大小为4字节,所以GCC保持其大小不变。扩展完成后,结构体的大小为:4+4+4=12字节。
3、强制设定对齐方式
字节对齐有利于整块读取数据,提高数据吞吐量,但是这是在牺牲存空间的情况下得到的,而在实际应用中,比如网络环境下,为了减少数据传输量,我们并不希望使用字节对齐方式,这时需要关闭字节对齐。
在程序的移植过程中,比如将32bit系统下的程序移植到64bit系统下,原有的字节对齐方式可能无法达到提高数据吞吐的目的,因此,我们需要更改字节对齐方式。
如何关闭?如何更改?
GCC提供了几种方式:
3.1、通过属性方式改变:
__attribute__ ((__ms_struct__)) 指定使用MS对齐策略
__attribute__ ((__gcc_struct__)) 指定使用GCC对齐策略
__attribute__ ((__packed__)) 指定使用最少存储空间对齐策略:合并所有位、对变量使用单字节方式对齐。
3.2、通过标识符声明方式:
#pragma pack(N) 设定对齐方式为N字节。
#pragma pack() 设定对齐方式为上一次对齐方式。
#pragma pack(push[, n]) 保存当前对齐方式并设置对齐方式为N字节。
#pragma pack(pop) 恢复最近一次保存的对齐方式。
对于i386构架,GCC专门提供了如下3个声明控制对齐方式:
#pragma ms_struct on 启用MS对齐方式。
#pragma ms_struct off 关闭MS对齐方式。
#pragma ms_struct reset 重置当前对齐方式为默认对齐方式。
3.3 通过设置GCC编译参数方式:
-fpack-struct[=N] 设定对齐方式为N字节对齐,不带”=N“时默认为4字节对齐。
-mno-ms-bitfields 关闭MS对齐策略,使用GCC对齐策略。
本文已收录于以下专栏:
相关文章推荐
ffmpeg在代码中大量的使用了字节对齐,甚至在有些成员变量中也定义了字节对齐的宏,使得在看代码时,有时很发难。,特整理如下。
字节对齐: 为什么要对齐:就是为了提高CPU的访问速度或把gcc默认的...
Taiga是一种项目管理器,网络上关于taiga的安装部署资料特别少,本人在安装taiga过程中也碰了不少壁,为方便大家安装taiga,我自己写了份安装手册如下:
(原文是用txt文件写的,放在论坛上...
标签: 
分类: 编程技术
http://blog.csdn.net/djzhao/article/details/
GCC 4.7+ version 字节对齐
转自:点击打开链接
一、概念 
   
   对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变...
【作者:孟祥月  博客:http://blog.csdn.net/mengxiangyue】
import java.awt.C
作者:黄聪
出处:/huangcong/
黄聪:phpexcel中文教程-设置表格字体颜色背景样式、数据格式、对齐方式、添加图片、批注、文字块、合并拆分...
他的最新文章
讲师:王禹华
讲师:宋宝华
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)相关文章推荐:
XCODE 代码管理
xcode下cocos2d-x的Resource的子目录资源读取路径问题
如果在Resource中直接添加一个文件夹文件,它的图标是黄色的,这样是不可以正常读取到文件夹中的资源文件的
解决办法是在把资源以目录的形式加入xcode时, 选择&Create folder references for any added folders&, 而不是默认的&
Create groups for any added folders &, 加入后,xcode左侧树状结构内显示的图标是蓝色而非黄色
都知道xcode里的新建的一个分组文件夹(黄...
阅读(0) 回复(0)
其他文章推荐
最近开始使用mac开发,xcode4.6自带了git和svn, 非常的方便,xcode4.5与此类同.
选择菜单 &File/Source Control/Repositories& 打开版本控制的属性页, 如下:
点击左下角的小加号&+&,
在弹出菜单选择 &Checkout or Cloe Repositories&, 在弹出页面中输入svn地址, 如下图所示:
值得说明的是, 这里的url要写到要checkout的项目, 只填写svn根目录在下一步认证时会报错的.
阅读(150) 回复(0)
类似如下的结构体:
typedef struct tagBITMAPFILEHEADER {
bfReserved1;
bfReserved2;
DWORD bfOffB
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
在vc下,sizeof(BITMAPFILEHEADER)是14.
在xcode下,是16.
#pragma pack(1)
xcode不支持,再搜索一下,在这里找到了答案:
/questions//pragma-pack1-nor-attribute-aligne...
阅读(90) 回复(0)
CMD + N: 新文件
CMD + SHIFT + N: 新项目
CMD + O: 打开
CMD + S: 保存
CMD + SHIFT + S: 另存为
CMD + W: 关闭窗口
CMD + SHIFT + W: 关闭文件
CMD + [: 左缩进
CMD + ]: 右缩进
CMD + CTRL + LEFT: 折叠
CMD + CTRL + RIGHT: 取消折叠
CMD + CTRL + TOP: 折叠全部函数
CMD + CTRL + BOTTOM: 取消全部函数折叠
CTRL + U: 取消全部折叠
CMD + D: 添加书签
CMD + /: 注释或取消注释
CTRL + .: 参数提示
阅读(30) 回复(0)
随着智慧型行动装置的日益普及,行动平台上的软体,也就是我们俗称的APP也渐渐成为软体开发者不可忽视的一个新兴领域。然而,在众多行动装置中,又以iOS及Android为两大主流平台,更是各软体开发者必须投入的两大区块。本篇所要介绍的xcode正是对於开发iOS平台APP来说最佳的开发工具,希望经过本篇的介绍及解说,可以带领您进入开发行动平台APP的行列,并且尝试设计您的第一款iPhone/ iPad程式。
一、xcode简介
xcode是一个由苹果...
阅读(0) 回复(0)
from: /4190.html
xcode如果不破解,无法真机调试, 也无法编译真机Release文件,只有付费开通Apple开发者账号,才能申请真机调试。而xcode进行破解后,结合越狱的iPhone或iPad, 即可免官方证书开发调试了!
生成本机证书
应用程序-&其他-&钥匙串访问
菜单:钥匙串访问-&证书助理-&创建证书, 然后按以下图片顺序操作即可:
xcode 5免证书开发调试_输入证书名称iPhon...
阅读(0) 回复(0)
xcode4.0以后,编译器换成了LLVM 编译器 2.0
与以前相比,更加强大:
1.LLVM 编译器是下一带开源的编译技术.完全支持C, Objective-C, 和 C++.
2.LLVM 速度比 GCC快两倍,建立的程序也会运行的更快. 因为它更好的利用现代的芯片的结构.
3.LLVM和xcode 4完全的整合在一起.包括关键字高亮,代码完整性等全都是由LLVM语法分析器来分析的. 这样可以在编辑的时候就可以很好的了解你的代码.
编译器进化之后,控制台调试命令前缀,...
阅读(0) 回复(0)
xcode 内置了 OCUnit 单元测试框架,但目前最好用的测试框架应该是 GHUnit。通过 GHUnit + OCMock 组合,我们可以在 iOS 下进行较强大的单元测试功能。本文将演示如何在 xcode 4.2 下使用 OCUnit, GHUnit 和 OCMock 进行单元测试。
在 xcode 下新建一个 OCUnitProject 工程,选中 Include Unit Tests 选择框,
OCUnit 框架则会为我们自动添加 Unit Test 框架代码:
xcode 在 OCUnitProjectTests.m ...
阅读(0) 回复(0)
ICommand+N will create a new file,
Command+Option+N will create a new group,
Command+B will build your project,
Command+Rwill run it.
阅读(0) 回复(0)
由于boost库在项目开发过程中使用的非常的频繁,且被成为“准标准库”,业界对其认可度较高,因此为了防止每次创建项目时都更改一次project配置,我决定将boost头文件和编译好的库统一放在xcode的默认搜索路径下。那么如何查看xcode的默认搜索路径呢?
#include&libxml/parser.h&
For testing, I ran:
echo '#include &libxml/parser.h&'| xcrun clang++ -xc -v -
which gives me
#include&...& search starts here...
阅读(90) 回复(0)
xcode文档的结构
如上图,打开后,整个文档界面有左面的侧栏和右面的内容区域构成。左面的侧栏可以选择不同的文档库。你的xcode里面一般来说有一组不同版本的iOS文档库、一组不同版本的OS X文档库,以及一个xcode文档库。
如果你这里没有你要查看的文档库,你可以选择xcode的Preferences菜单,然后选择Downloads -& Documentation。在这里可以看到已经下载安装了的文档库,还没有下载的文档库,可以酌...
阅读(0) 回复(0)
盛拓传媒:
北京皓辰网域网络信息技术有限公司. 版权所有
北京市公安局海淀分局网监中心备案编号:
广播电视节目制作经营许可证:编号(京)字第1149号
ITPUB推荐文章解答你所有技术难题&h2&&b&回顾&/b&&/h2&&p&我们先思考一个问题:iOS11 之前创建哪类动画最麻烦?&/p&&p&答:交互式动画和自定义的timingFunction动画。&/p&&p&无code无真相。我们先来看看早先版本的动画接口是如何实现交互式动画和自定义timingFunciton的。&/p&&h2&&b&如何实现一个交互式动画?&/b&&/h2&&p&大家知道,iOS里面动画的实现方式主要是两种,一种是UIViewAnimation和基于Layer层的CAAnimation。&/p&&p&两种动画的区别很多,当然,符合越底层的接口自由度越高的这个特点。CAAnimation的可定制性更强,但是在我看来,两种动画最主要的区别用一句话形容,就是.&/p&&p&&code&UIViewAnimation是开弓没有回头箭。CAAnimation是流星锤,可收可放。&/code&&/p&&p&我们现在,就来实现一个用手势控制的动画。效果如图。&br&&/p&&img src=&/50/v2-03cb5c46fe2c643a793f_b.jpg& data-caption=&& data-rawwidth=&618& data-rawheight=&1142& class=&origin_image zh-lightbox-thumb& width=&618& data-original=&/50/v2-03cb5c46fe2c643a793f_r.jpg&&&p&&br&&/p&&p&我们的目的是利用UISlider控制动画的进度,这个动画就是图片绕Y轴旋转。&br&代码如下。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class ViewController: UIViewController {
let imageView = UIImageView.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage.init(named: &wuyanzu.jpg&)
imageView.center = self.view.center
imageView.layer.transform.m34 = -1.0/500
self.view.addSubview(imageView)
let basicAnimation = CABasicAnimation.init(keyPath: &transform.rotation.y&)
basicAnimation.fromValue = 0
basicAnimation.toValue = CGFloat.pi
basicAnimation.duration = 1
imageView.layer.add(basicAnimation, forKey: &rotate&)
imageView.layer.speed = 0
// Do any additional setup after loading the view, typically from a nib.
@IBAction func sliderValueChanged(sender:UISlider) {
imageView.layer.timeOffset = CFTimeInterval(sender.value)
&/code&&/pre&&/div&&p&在iOS11之前,可交互动画的原理很简单。过程总结如下。&br&1. 将layer的speed设置为0,这样,动画就处于暂停状态&br&2. 利用timeOffset来控制整个动画的进度&/p&&p&再举个例子,如果这个动画不是利用UISlider控制旋转角度,而是利用PanGesture移动的距离来控制呢?&/p&&p&那么这种情况,你需要找到的就是手势的距离和Rotate动画timeOffset的一种关联。&/p&&p&我利用Sketch做了一个简陋的草图来模拟这种情况。&/p&&p&&br&&/p&&img src=&/50/v2-40acc3f30c7ae2be21b76_b.jpg& data-caption=&& data-rawwidth=&836& data-rawheight=&1436& class=&origin_image zh-lightbox-thumb& width=&836& data-original=&/50/v2-40acc3f30c7ae2be21b76_r.jpg&&&p&&br&&/p&&p&其实看完图片我们已经可以建立起手势移动距离和timeOffset的关联。&br&以横向移动为前提,那么&code&手指的x坐标/图片的width 总是 &= 1.0&/code&,所以,当旋转动画的总时长为1,那么动画的进度timeOffset就恰好等于x/imageView.width了。完美的关联了起来。&/p&&h2&&b&问题&/b&&/h2&&p&我们也看到了这种处理方法的弊端。就是,实在太繁琐了。&/p&&p&所以,在今年的wwdc里,苹果为我们提供了一种非常方便的解决方案。&/p&&h2&&b&UIViewPropertyAnimator&/b&&/h2&&p&其实在iOS10,苹果已经引入了另外一种基于View层的强大的动画框架,&code&UIViewPropertyAnimator&/code&.&/p&&p&他提供了一个非常棒的方法来解决以前自定义timingFunction只能由CAAnimation来处理的问题。&/p&&h2&&b&timingFunction&/b&&/h2&&p&说到timingFunction,相信写过动画的人都非常清楚系统提供的几种。&/p&&ul&&li&Liner (线性)&/li&&li&EaseIn (先慢后快)&/li&&li&EaseOut (先快后慢)&/li&&li&EaseInEaseOut (慢进,加速,减速)&/li&&/ul&&p&实际上这几种timingFunction只能说是勉强够用。当你想更细致调整动画速率的时候势必会使用自定义的贝塞尔曲线来控制动画速率。&/p&&p&比如在&code&/&/code&,我创建了一个自定义的曲线。&br&&/p&&img src=&/50/v2-dc9644eadf408ba63af7_b.jpg& data-caption=&& data-rawwidth=&2048& data-rawheight=&864& class=&origin_image zh-lightbox-thumb& width=&2048& data-original=&/50/v2-dc9644eadf408ba63af7_r.jpg&&&p&&br&&/p&&p&他的control point 分别是(0.17, 0.67, 0.71, 0.15)&br&那么,如果你想用这个贝塞尔曲线当做timingFunction,在iOS10之前你只能利用CABasicAnimatin来实现。&/p&&p&例如,第一个旋转动画自定义timingFunction是这样的。&/p&&p&&code&basicAnimation.timingFunction = CAMediaTimingFunction.init(controlPoints: 0.17, 0.67, 0.71, 0.15)&/code&&/p&&p&想在View层自定义timingFunction?&br&没门。&/p&&p&所幸,我们在iOS10的时候拥有了&code&UIViewPropertyAnimator&/code&&/p&&p&现在,我们如此简单的就创建了一个自定义动画速率的动画。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&let convenienceAnimator = UIViewPropertyAnimator.init(duration: 0.66, controlPoint1: point1, controlPoint2: point2) {
convenienceAnimator.addCompletion({ (position) in
if position == .end {
convenienceAnimator.startAnimation()
&/code&&/pre&&/div&&h2&&b&iOS11中更强大的UIViewPropertyAnimator&/b&&/h2&&p&session 230中,苹果着重介绍了我们梦寐以求的简单方便的交互式动画api。&/p&&p&举一个session 230中的例子来看一下新版本中如何实现交互式动画。&/p&&p&&br&&/p&&img src=&/50/v2-be5dec383a7e5fcded5b13a_b.jpg& data-caption=&& data-rawwidth=&782& data-rawheight=&1348& class=&origin_image zh-lightbox-thumb& width=&782& data-original=&/50/v2-be5dec383a7e5fcded5b13a_r.jpg&&&p&&br&&/p&&p&这里,我们需要用手势来控制动画的进度。这里,动画是让小球从左向右移动100的距离。&/p&&p&看看代码如何简单的将动画和手势关联起来。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var animator:UIViewPropertyAnimator!
var circle:UIImageView!
func handlePan(recognizer:UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
animator = UIViewPropertyAnimator.init(duration: 1, curve: .easeOut, animations: {
self.circle.frame = self.circle.frame.offsetBy(dx: 100, dy: 0)
animator.pauseAnimation()
case .changed:
let translation = recognizer.translation(in: self.circle)
animator.fractionComplete = translation.x/100
case .ended:
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
&/code&&/pre&&/div&&ol&&li&手势开始的时候创建animator。然后暂停,在这里,动画暂停的本质同样是将Layer的speed设置为0。&/li&&li&动画的完成率等同于手势移动的距离除以总距离。&/li&&li&当手势结束的时候,我们调用了continueAnimation让动画继续执行到结束。其实这种需求比较少见,最常见的应该是当手势结束的时候让动画停留在这个阶段而不是继续进行动画。&/li&&/ol&&p&在这里,我们改造一下这个动画,让它更符合我们的用户习惯。&/p&&p&首先,在手势事件的外部定义好这个animator。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&circle.backgroundColor = UIColor.red
circle.layer.cornerRadius = 10
circle.frame = CGRect.init(x: 10, y: 100, width: 20, height: 20)
circle.isUserInteractionEnabled = true
self.view.addSubview(circle)
animator = UIViewPropertyAnimator.init(duration: 1, curve: .easeOut, animations: {
self.circle.frame = self.circle.frame.offsetBy(dx: 100, dy: 0)
animator.pauseAnimation()
&/code&&/pre&&/div&&p&然后,手势的事件代码如下。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&func handlePan(recognizer:UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
progress = animator.fractionComplete
case .changed:
let translation = recognizer.translation(in: self.circle)
animator.fractionComplete = translation.x/100 + progress
case .ended:
&/code&&/pre&&/div&&p&在这里,我们多了一个叫做progress的变量,这个变量的作用就是记录当前动画的进度,在每次手势变化的时候,让动画保持连贯性。不然,每一次动画都重新执行了。&br&建议同学们这里自己用代码试验一下效果。&/p&&h2&&b&出现了一些问题?&/b&&/h2&&p&话说讲到这里,我不知道有没有同学会对一个非常重要的问题感到疑惑。&br&什么问题呢?&br&就是&b&创建animator的时候的timingFunction是EaseOut,先快后慢,那么理论上应该是手势移动了一半,动画早就进行的超过了一半才对。&/b&&/p&&p&因为EaseOut的动画曲线是这样的&/p&&p&&br&&/p&&img src=&/50/v2-a339a248b7c_b.jpg& data-caption=&& data-rawwidth=&698& data-rawheight=&722& class=&origin_image zh-lightbox-thumb& width=&698& data-original=&/50/v2-a339a248b7c_r.jpg&&&p&&br&&/p&&p&注意看这张图的横纵坐标。&br&X坐标代表Time的进度,Y坐标代表动画的进度。&br&当X走到51%的时候,动画已经进行了72%。&br&在我们的场景中,这意味着,当手势移动了51个pixel的时候,circle这个view已经跑了72个pixel。&/p&&p&想想这会造成什么问题?&/p&&p&问题就是,用户在交互的时候完全摸不着头脑。&/p&&p&再举个形象的例子。&/p&&p&加入有一个UISlider控制一个Animator的进度,这个Animator是作用于View的透明度Alpha从1到0。&/p&&p&然后Animator的timingFunction是EaseOut,那么用户拖动UISlider的结果很可能是Slider还没滑动到底,这个View的alpha已经变成了0.&/p&&p&为了避免这种情况,当你的Animator是Interactive状态的时候,苹果会自动把你的timingFunction转变为&code&Linear&/code&.&/p&&p&如图&br&&/p&&img src=&/50/v2-ed9c2e5d4e3ec07cf3a8e8_b.jpg& data-caption=&& data-rawwidth=&1350& data-rawheight=&1186& class=&origin_image zh-lightbox-thumb& width=&1350& data-original=&/50/v2-ed9c2e5d4e3ec07cf3a8e8_r.jpg&&&p&&br&&/p&&p&那么如果你真的希望可交互式动画的timingFunction不是自动转变为Liner,能不能做到呢?&/p&&p&答案是可以的。&br&苹果在iOS11中为UIViewPropertyAnimator提供了一个Bool值&code&scrubsLinearly&/code&,只要设置为No,那么动画就会按照你设置的timingFunction执行了。&/p&&h2&&b&第二个问题,动画执行完了怎么办?&/b&&/h2&&p&其实在手势执行完毕的时候,调用&code&animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)&/code&&br&会将动画执行完成,但是有一个问题是,动画一旦执行完成,动画的状态就会从Interactive转变为Active,也就是说,不可以再进行交互了。这时候,你需要把animator的pauseOnCompletion设置为false。那么动画就会一直保持Interactive状态了。&/p&&h2&&b&SpringAnimation&/b&&/h2&&p&说实话,这期session中,让我比较失望的就是苹果对于SpringAnimation的支持只是简单地增加了一个under-damping的概念。并没有加入springAnimation中很重要的两个属性。&/p&&ul&&li&Fricition&/li&&li&Tension&/li&&/ul&&p&为什么这两个属性非常重要。这里,我需要给大家介绍一个国外非常流行的app。&b&Principle&/b&&/p&&p&他是国外做交互式prd的非常好用的一个app,我最近在做的一个app在做交互原型的时候大量的使用了这个app。&/p&&p&我们来看看这个app中对于spring动画的一些设置。&/p&&p&&br&&/p&&img src=&/50/v2-5e043b6a115bae1e358ae7_b.jpg& data-caption=&& data-rawwidth=&640& data-rawheight=&488& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&/50/v2-5e043b6a115bae1e358ae7_r.jpg&&&p&&br&&/p&&p&用damping这个参数调spring最大的问题就是.....无法当伸手党,直接拿来参数用。&br&所以,目前来说,最好用的SpringAnimation还是facebook得pop。&/p&&p&比如...... 一个pop伸手党的日常是这样的。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&let alphaSpring = POPSpringAnimation.init(propertyNamed: kPOPViewAlpha)
alphaSpring?.fromValue = 0.67
alphaSpring?.toValue = 1
alphaSpring?.dynamicsFriction = 20.17
alphaSpring?.dynamicsTension = 381.47
alphaSpring?.delegate = self
alphaSpring?.name = &alpha&
self.pop_add(alphaSpring, forKey: &alpha&)
&/code&&/pre&&/div&&p&只能说,用pop好省心。&/p&&h2&&b&补充&/b&&/h2&&p&&b&cornerRadius终于可动画了。&/b&&/p&&h2&&b&提出两个问题&/b&&/h2&&ol&&li&iOS11之前真的没有支持手势交互的api么?&/li&&li&如果存在这样的api,那么这个api的原理是什么呢?是怎样实现无论是UIViewAnimation还是CABasicAnimation都能无缝和手势关联的呢?&/li&&/ol&&p&这是两个很有意思的问题,大家有空可以思考一下。&/p&&h2&&b&参考&/b&&/h2&&blockquote&&i&&a href=&/?target=https%3A///videos/play/wwdc/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Session 230&i class=&icon-external&&&/i&&/a&&/i&&/blockquote&&h2&&b&文章收录在WWDC 2017内参里&/b&&/h2&&p&有闲钱的可以支持一下这本书&br&&a href=&/?target=https%3A///item.htm%3Fid%3D& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/item.ht&/span&&span class=&invisible&&m?id=&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&
回顾我们先思考一个问题:iOS11 之前创建哪类动画最麻烦?答:交互式动画和自定义的timingFunction动画。无code无真相。我们先来看看早先版本的动画接口是如何实现交互式动画和自定义timingFunciton的。如何实现一个交互式动画?大家知道,iOS里面动画的实…
&img src=&/50/v2-7ddfdd02afbc56d1588f33_b.png& data-rawwidth=&2076& data-rawheight=&674& class=&origin_image zh-lightbox-thumb& width=&2076& data-original=&/50/v2-7ddfdd02afbc56d1588f33_r.png&&&p&(ASDK已改名Texture)&/p&&p&说到视图性能,不能不提到UITableView,对于它的滚动性能的讨论和优化从未停止。在我们的探索过程中,尝试过以下一些措施:&/p&&ul&&li&cell reuse,Apple原生支持,每个人都会用到&/li&&li&estimated cell height,iOS8开始原生支持&/li&&li&手动将计算完成的height缓存(或使用FDTemplateLayoutCell等框架自动计算)&/li&&li&prefetch API,iOS10开始原生支持&/li&&li&异步加载cell内容,文字图片等&/li&&/ul&&p&还有一些诸如圆角、opaque等普通UIView可能遇到的性能瓶颈已经在&a href=&/?target=https%3A///yxztj/TechBlogs/blob/master/asdk-1.md& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&第一篇&i class=&icon-external&&&/i&&/a&中讨论了一些,这里不再赘述。&/p&&p&然而我们会想,cell的异步布局、图片和文字渲染是否还可以优化,预加载是否可以更完善更智能?仍然有许多问题等待被解决。&/p&&h2&UITableView加载Cell的过程&/h2&&p&我们先看一下一般UITableView加载Cell的过程:&/p&&ol&&li&cellForRowAtIndexPath,读取model&/li&&li&从reuse池中dequeue一个cell,调用prepareForReuse重置其状态&/li&&li&将model装配到UITableViewCell当中去&/li&&li&布局(耗时且无法缓存的Autolayout),渲染(文字和图片都很耗时),显示&/li&&/ol&&p&这些操作都在cell将要进入屏幕的一刹那发生,不难理解,在短短的16ms里(60fps)是很难完成这些任务的,尤其是当用户快速滚动的时候,大量任务堆积在主线程runloop,情况变得雪上加霜。如果将滚动中的CPU占用情况用图表显示出来,大概是这样的(WWDC16 session 219):&/p&&img src=&/50/v2-f28f17ff0d010_b.jpg& data-rawwidth=&2646& data-rawheight=&630& class=&origin_image zh-lightbox-thumb& width=&2646& data-original=&/50/v2-f28f17ff0d010_r.jpg&&&p&这其中每当一个cell将要进入屏幕,一个尖峰就会产生。而在其他相对空闲的时候,cpu的负载相当的低。&/p&&p&自然我们会想到,如果把任务平均分配到一个时间段内,而不是集中在某一个点,是否就可以避免这样的情况发生?如果我们能够&b&预测&/b&一个cell很快将要进入屏幕,而此时cpu空闲,是否可以未雨绸缪,提前做一些布局和渲染的工作?那样一来,在cell真正需要显示的时候,由于布局和渲染结果backing store已经是现成的了,只需要将它送给真正负责显示的view就可以,也就可以避免产生剧烈的性能波动。&/p&&h2&ASTableNode/ASCollectionNode开辟的新航路&/h2&&p&首先的好消息是,作为ASDK的一员,ASTableNode以及其cellNode已经具备了异步布局和异步渲染的能力,即使没有做额外优化,仅仅利用ASDK通用的异步机制将耗时操作延后,相对于一般UITableView已经有了显著的提升。虽然性能锯齿仍然存在,但是将其转移到了后台线程以后,用户感受到的卡顿就已经不会那么明显了。&/p&&p&然而这些似乎还不够,在进入屏幕之后才开始渲染,会有短暂的白屏现象(等待渲染完成)再显示内容。&b&既然渲染工作可以在显示之后再进行,那么类似的,也可以在显示之前的一段时间,把布局和渲染的工作预先完成&/b&。&/p&&p&要达到这些目的,首先介绍一些相关的类:&/p&&ul&&li&ASTableNode/ASCollectionNode,可以认为是UITableView/UICollectionView的异步版本,内部包装了原来的UIKit的对应版本,并扩展了一系列功能使他们能够实现异步布局及渲染。&/li&&li&ASInterfaceState,表示一个node不同的显示状态。其实每个ASDisplayNode都具备interfaceState属性,它主要的用武之地还是在tableNode/collectionNode之中。对于一个UITableViewCell来说,布局和渲染一般都是在cellForRowAtIndexpath同时完成,然而当需要精细处理任务时就需要把每一个不同的状态分开,降低某一瞬间由于CPU负载高导致卡顿的可能性。ASInterfaceState递进地分为5种状态:&/li&&li&None,该node在一段时间内不会进入屏幕&/li&&li&MeasureLayout,可能会在一段时间后进入屏幕,应该准备layout和size计算&/li&&li&Preload,加载所需要的数据,如下载图片,缓存读取等等&/li&&li&Display,马上将要进入屏幕,开始进行渲染操作,显示包含的文字或者图片&/li&&li&Visible,该node(所对应的view)至少有1个像素已经在屏幕内,正在显示&/li&&/ul&&p&对于每一个cell而言,原本需要在同一时间点进行的所有初始化/加载/布局/渲染等工作,现在被均匀分配到了不同的状态进行预处理。随着用户滚动列表,根据cell离屏幕的距离不同,设置相应的interfaceState并触发不同阶段的工作,达到均匀分配的目的。同时,由于不需要在主线程上进行,多个cell的工作可以通过共享后台线程来大幅提高并行效率。&/p&&ul&&li&ASDataController,与ASTableNode一一对应,负责代替ASTableNode管理delegate和dataSource的一系列方法,诸如初始化,插入,删除和一些代理方法等。&/li&&li&ASRangeController,同样与ASTableNode一一对应,并且可以根据设备性能自定义布局、加载、渲染的工作indexPath区间,在滚动时动态高效地调整各cell的interfaceState来层层触发不同显示阶段的工作,对于流畅滚动起到了至关重要的作用。&/li&&/ul&&img src=&/50/v2-689a24cb8cec77f273c18_b.jpg& data-rawwidth=&497& data-rawheight=&719& class=&origin_image zh-lightbox-thumb& width=&497& data-original=&/50/v2-689a24cb8cec77f273c18_r.jpg&&&ul&&li&ASScrollDirection,定义了列表滚动的方向(上下左右)。在ASRangeController调整各阶段的工作区间时,一般在用户滚动的方向上需要多加载一些,而滚出屏幕的cell在一定时间内回到屏幕的概率较低,因此其分配到的资源也就相应少一些。&/li&&/ul&&p&在ASDK1.x的时代,由于彼时还没有ASRangeController的存在,cell的渲染只会在进入屏幕以后进行,也就是说,虽然性能能够达到60fps,但是滚动较快时,渲染跟不上,『白屏』现象就出现了。到了2.x有了ASRangeController之后,虽然在滚动极快的情况下仍然会因为资源不足而产生白屏现象,但是在一般情况下,因为资源分配更加合理,这个问题得到了显著的改善。&/p&&h2&一些细节&/h2&&h2&多线程&/h2&&p&当同时layout多个node时,如何均匀分配工作到各个线程,同时单次不占用过多cpu时间?&/p&&p&ASDK是这么做的:&/p&&ol&&li&获取当前设备上cpu的数量,并乘以每个cpu的工作量,如4 * 5 = 20,即同一批最多对20个node进行布局。(尽管没有找到严格的文档来说明这样的计算方式会带来最高的效率,但是应该要比不分批次处理更优,使占用的cpu时间片可控)&/li&&li&调用dispatch_apply,对同一批次20个node进行并行布局计算&/li&&li&每一批处理20个,直到所有都处理完&/li&&/ol&&h2&监听状态变化&/h2&&p&不光ASDisplayNode本身会根据不同的state进行相应的工作,它同时也提供了一系列的方法供子类override,如didEnterPreloadState/didExitVisibleState等等。在实际应用中,由于子类通常会持有一些自己管理的资源(如图片),需要控制在显示/离开屏幕之际进行资源的分配/回收。由于每个时间点分的比较细,只要将工作均匀、合理地分配到相应的方法中,就可以实现非常精确高效的资源调度。&/p&&h2&内存管理&/h2&&p&ASRangeController经常需要管理屏幕外的node(可能同时有好几屏的内容同时进行布局计算和显示),通过预处理来减轻将来的工作量,是一个典型的『空间换时间』的办法,对于内存的压力自然就会上升。&/p&&p&为此,ASRangeController提供了一些参数,让开发者可以自行决定每个stage所囊括的范围,达到控制内存的:&/p&&ul&&li&ASLayoutRangeModeFull,此时用到的资源较多,同时用户体验也是最好的&/li&&li&ASLayoutRangeMinimum,比上一种类型节省一些资源&/li&&li&ASLayoutRangeModeVisibleOnly,在app退到后台的时候自动设置,将屏幕外的node所占用的资源释放,降低app在系统中被杀的概率&/li&&li&ASLayoutRangeModeLowMemory,比上一种更省内存,对于app退到后台,并且目前没有在屏幕上(可能在navigation stack里)的rangeController适用,最大程度释放资源&/li&&/ul&&p&我们可以通过调用setTuningParameters的方式,对于每一种mode中的每一种layoutRangeType做出精细调整。同时,ASDK会自动根据此时node在屏幕上的情况,自动在以上几种mode中来回切换,并根据指定的参数范围来加载/释放资源,达到资源和性能的平衡。&/p&&p&ASDK中还自带了一个显示rangeController工作情况的小工具:&/p&&img src=&/50/v2-7a149af7b4eca7ff3a00cd_b.jpg& data-rawwidth=&748& data-rawheight=&1338& class=&origin_image zh-lightbox-thumb& width=&748& data-original=&/50/v2-7a149af7b4eca7ff3a00cd_r.jpg&&&p&即刻在热门页面有4个tab,相应的右下角的debug view显示的分别是4个页面不同的working range。箭头表示当前的滚动方向,由于我们配置了在滚动方向上加载的距离比反方向要更长,因此可以看到在滚动方向上的色块会更长一些。&/p&&h2&即刻的实践&/h2&&p&即刻iOS在最初时也曾苦苦找寻列表性能优化方案。在阅读了Ray Wenderlich的&a href=&/?target=https%3A///124311/asyncdisplaykit-2-0-tutorial-getting-started& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&一篇文章&i class=&icon-external&&&/i&&/a&之后,惊艳其出色的性能,开始接触ASDK。最开始尝试在消息页面使用,后来在长期实践中,发现其确实能够解决tableView的行高计算和性能问题,才渐渐在其他页面使用。&/p&&p&就如同在本系列第一篇提到的,对于先进的框架虽然不能重度依赖,我们也愿意勇于拥抱其先进之处。站在ASDK的角度看UITableView,就能有更大的空间来重新审视列表滚动的本质,将性能和资源分配效率提升到一个新的高度;即使将来脱离ASDK,仍然有一些想法值得我们进一步思考。&/p&&p&想体验即刻的朋友可以下载看一下,最近又增加了不少新玩法 :)&/p&&p&PS: 我们在招Android、爬虫、后端的职位,链接:&a href=&/?target=http%3A///careers& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&/caree&/span&&span class=&invisible&&rs&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a& &/p&
(ASDK已改名Texture)说到视图性能,不能不提到UITableView,对于它的滚动性能的讨论和优化从未停止。在我们的探索过程中,尝试过以下一些措施:cell reuse,Apple原生支持,每个人都会用到estimated cell height,iOS8开始原生支持手动将计算完成的height…
&img src=&/50/v2-902c3c987f3bb47fcc70927_b.jpg& data-rawwidth=&557& data-rawheight=&332& class=&origin_image zh-lightbox-thumb& width=&557& data-original=&/50/v2-902c3c987f3bb47fcc70927_r.jpg&&&p&商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。&/p&&p&原文链接:&a href=&/?target=http%3A///lab/view/326.html%3Ffrom%3Dcontent_zhihu& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&/lab/view/326.html&i class=&icon-external&&&/i&&/a&&/p&&p&&br&&/p&&p&&b&WeTest 导读&/b&&/p&&p&iOS 11 为整个生态系统的 UI 元素带来了一种更加大胆、动态的新风格。 本文介绍了iOS11在UI方面做了哪些更新,有些更新可以为用户提供更加完美的体验,但也有的可能会给目前的APP带来异常bug。&/p&&p&&br&&/p&&p&&b&前言&/b&&/p&&p&前几天发现在做的APP在iOS11系统上动画有异常,在其他系统的设备上都是正常的,动画的操作是观察tableView的contentOffset变化后执行的,异常动画发生在tableView reloadData之后,也就是说tableView reloadData之后,tableView的contentOffset发生了几次变化。查了下资料发现原因是iOS11中默认开启了Self-Sizing,在WWDC 2017 session204 Updating Your App for iOS 11 中有介绍,因此研究了下这个session,本文作为一个总结,下文的第三部分会有对上述的动画异常的原因分析及解决方式。&/p&&p&&br&&/p&&p&本文内容包括:集成了搜索的大标题栏、横向选项卡栏、Margins 和 Insets以及 UIScrollView和UITableView 的更新和功能更强大的滑动操作。&/p&&p&&br&&/p&&p&&b&一、在UIKit’s Bars中加入的新功能&/b&&/p&&p&WWDC通过iOS新增的文件管理App:Files开始介绍,在Files这个APP中能够看到iOS11中UIKit’s Bars的一些新特性:在浏览功能上的大标题视图(向上滑动后标题会回到原来的UI效果)、横屏状态下tab上的文字和icon会变为左右排列。我用iOS11的模拟器体验了一下Files这个APP的竖屏和横屏,如下图所示:&/p&&p&&br&&/p&&img src=&/50/v2-7d26fad43a5a_b.png& data-rawwidth=&289& data-rawheight=&517& class=&content_image& width=&289&&&p&&br&&/p&&img src=&/50/v2-aa76a3d17_b.png& data-rawwidth=&514& data-rawheight=&289& class=&origin_image zh-lightbox-thumb& width=&514& data-original=&/50/v2-aa76a3d17_r.png&&&p&&br&&/p&&p&(command+向左的箭头让模拟器横屏)&/p&&p&&br&&/p&&p&横屏时,在iPhone上,tab上的图标较小,tab bar较小,这样垂直空间可多放置内容。如果有人看不清楚tab bar上的图标或文字,可以通过长按tab bar上的任意item,会将该item显示在HUD上,这样可以清楚的看清icon和text。对tool bar 和 navigation bar同理,长按item也会放大显示。如下图显示:&/p&&p&&br&&/p&&img src=&/50/v2-0be195d6f161c6021514_b.png& data-rawwidth=&595& data-rawheight=&274& class=&origin_image zh-lightbox-thumb& width=&595& data-original=&/50/v2-0be195d6f161c6021514_r.png&&&p&&br&&/p&&p&&b&1. UIBarItem&/b&&/p&&p&UIBarItem是UI tab bar item和UI bar button item的父类,要想实现上面介绍的效果,只需要为UIBarItem 设置landscapeImagePhone属性,在storyboard中也支持这个设置,对于HUD的image需要设置另一个iOS11新增的属性:largeContentSizeImage,关于这部分更详细的讨论,可以参考 WWDC2017 Session 215:What's New in Accessibility&/p&&p&&br&&/p&&p&&b&2. 控制大标题的显示&/b&&/p&&p&在UI navigation bar中新增了一个BOOL属性prefersLargeTitles,将该属性设置为ture,navigation bar就会在整个APP中显示大标题,如果想要在控制不同页面大标题的显示,可以通过设置当前页面的navigationItem的largeTitleDisplayMode属性;&/p&&p&&br&&/p&&img src=&/50/v2-032dbed759de3d5dfe74736_b.png& data-rawwidth=&654& data-rawheight=&253& class=&origin_image zh-lightbox-thumb& width=&654& data-original=&/50/v2-032dbed759de3d5dfe74736_r.png&&&p&&br&&/p&&p&&b&3. Navigation 集成 UISearchController&/b&&/p&&p&把你的UISearchController赋值给navigationItem,就可以实现将UISearchController集成到Navigation。&/p&&p&&br&&/p&&img src=&/50/v2-fbe77540f14_b.png& data-rawwidth=&655& data-rawheight=&86& class=&origin_image zh-lightbox-thumb& width=&655& data-original=&/50/v2-fbe77540f14_r.png&&&p&&br&&/p&&p&&b&4. UINavigationController和滚动交互&/b&&/p&&p&滚动的时候,以下交互操作都是由UINavigationController负责调动的:&/p&&p&&br&&/p&&img src=&/50/v2-ef1a80cf7d969e71b692f_b.png& data-rawwidth=&654& data-rawheight=&110& class=&origin_image zh-lightbox-thumb& width=&654& data-original=&/50/v2-ef1a80cf7d969e71b692f_r.png&&&p&&br&&/p&&p&所以,如果你使用navigation bar,组装push和pop体验,你不会得到searchController的集成、大标题的控制更新和Rubber banding效果,因为这些都是由UINavigationController控制的。&/p&&p&&br&&/p&&p&&b&5. UIToolbar and UINavigationBar— Layout&/b&&/p&&p&在 iOS 11 中,当苹果进行所有这些新特性时,也进行了其他的优化,针对 UIToolbar 和 UINavigaBar 做了新的自动布局扩展支持,自定义的bar button items、自定义的title都可以通过layout来表示尺寸。 需要注意的是,你的constraints需要在view内部设置,所以如果你有一个自定义的标题视图,你需要确保任何约束只依赖于标题视图及其任何子视图。当你使用自动布局,系统假设你知道你在做什么。&/p&&p&&br&&/p&&p&&b&6. Avoiding Zero-Sized Custom Views&/b&&/p&&p&自定义视图的size为0是因为你有一些模糊的约束布局。要避免视图尺寸为0,可以从以下方面做:&/p&&p&● UINavigationBar 和 UIToolbar 提供位置&/p&&p&● 开发者则必须提供视图的size,有三种方式: &/p&&p&① 对宽度和高度的约束;&/p&&p&② 实现 intrinsicContentSize;&/p&&p&③ 通过约束关联你的子视图;&/p&&p&&br&&/p&&p&&b&二、管理margins 和 insets&/b&&/p&&p&&br&&/p&&p&&b&1. layout margins&/b&&/p&&p&基于约束的Auto Layout,使我们搭建能够动态响应内部和外部变化的用户界面。Auto Layout为每一个view都定义了margin。margin指的是控件显示内容部分的边缘和控件边缘的距离。 可以用layoutMargins或者layoutMarginsGuide属性获得view的margin,margin是视图内部的一部分。layoutMargins允许获取或者设置UIEdgeInsets结构的margin。layoutMarginsGuide则获取到只读的UILayoutGuide对象。&/p&&p&&br&&/p&&p&在iOS11新增了一个属性:directional layout margins,该属性是NSDirectionalEdgeInsets结构体类型的属性:&/p&&p&&br&&/p&&img src=&/50/v2-9bcc6d32a785_b.png& data-rawwidth=&654& data-rawheight=&112& class=&origin_image zh-lightbox-thumb& width=&654& data-original=&/50/v2-9bcc6d32a785_r.png&&&p&&br&&/p&&p&layoutMargins是UIEdgeInsets结构体类型的属性:&/p&&p&&br&&/p&&img src=&/50/v2-d8f92207bfc762d4a6f4fde_b.png& data-rawwidth=&656& data-rawheight=&110& class=&origin_image zh-lightbox-thumb& width=&656& data-original=&/50/v2-d8f92207bfc762d4a6f4fde_r.png&&&p&&br&&/p&&p&从上面两种结构体的对比可以看出,NSDirectionalEdgeInsets 属性用leading 和 trailing 取代了之前的 left 和 right。&/p&&p&&br&&/p&&p&directional layout margins属性的说明如下:&/p&&p&&br&&/p&&img src=&/50/v2-644c0ffde4e1c9b5c36cef4_b.png& data-rawwidth=&656& data-rawheight=&83& class=&origin_image zh-lightbox-thumb& width=&656& data-original=&/50/v2-644c0ffde4e1c9b5c36cef4_r.png&&&p&&br&&/p&&p&&b&例子:&/b&当你设置了trailing = 30;当在一个right to left 语言下trailing的值会被设置在view的左边,可以通过layout margins的left属性读出该值。如下图所示:&/p&&p&&br&&/p&&img src=&/50/v2-928e19015bb25ddc5f7ec1a_b.png& data-rawwidth=&583& data-rawheight=&474& class=&origin_image zh-lightbox-thumb& width=&583& data-original=&/50/v2-928e19015bb25ddc5f7ec1a_r.png&&&p&&br&&/p&&p&还有其他一些更新。自从引入layout margins,当将一个view添加到viewController时,viewController会修复view的的layoutMargins为UIKit定义的一个值,这些调整对外是封闭的。从iOS11开始,这些不再是一个固定的值,它们实际是最小值,你可以改变你的view的layoutMargins为任意一个更大的值。而且,viewController新增了一个属性:viewRespectsSystemMinimumLayoutMargins,如果你设置该属性为&false&,你就可以改变你的layout margins为任意你想设置的值,包括0,如下图所示:&/p&&p&&br&&/p&&img src=&/50/v2-2a1b84067abb1b1efe20355_b.png& data-rawwidth=&659& data-rawheight=&311& class=&origin_image zh-lightbox-thumb& width=&659& data-original=&/50/v2-2a1b84067abb1b1efe20355_r.png&&&p&&br&&/p&&p&&b&2. 安全区域(Safe Area)&/b&&/p&&p&如下图:照片应用程序&/p&&p&&br&&/p&&img src=&/50/v2-8f13f9e25ef2e52ed84c64bba89a908d_b.png& data-rawwidth=&284& data-rawheight=&590& class=&content_image& width=&284&&&p&&br&&/p&&p&从iOS 7以来,我们在整个操作系统中都有这些半透明的bars,苹果鼓励我们通过这些bars绘制内容,我们是通过viewController 的edgesForExtendedLayout属性来做这些的。 iOS 7 开始,在 UIViewController中引入的 topLayoutGuide和 bottomLayoutGuide 在 iOS 11 中被废弃了,取而代之的就是safeArea的概念,safeArea是描述你的视图部分不被任何内容遮挡的方法。 它提供两种方式:safeAreaInsets或safeAreaLayoutGuide来提供给你safeArea的参照值,即 insets 或者 layout guide。 safeArea区域如下图所示:&/p&&p&&br&&/p&&img src=&/50/v2-0cf300a653ff4a6539466b_b.png& data-rawwidth=&662& data-rawheight=&429& class=&origin_image zh-lightbox-thumb& width=&662& data-original=&/50/v2-0cf300a653ff4a6539466b_r.png&&&p&&br&&/p&&p&如果有一个自定义的viewController,你可能要添加你自己的bars,增加safeAreaInsets的值,可以通过一个新的属性:addtionalSafeAreaInsets来改变safeAreaInsets的值,当你的viewController改变了它的safeAreaInsets值时,有两种方式获取到回调:&/p&&p&&br&&/p&&img src=&/50/v2-b9e84e9fb_b.png& data-rawwidth=&654& data-rawheight=&80& class=&origin_image zh-lightbox-thumb& width=&654& data-original=&/50/v2-b9e84e9fb_r.png&&&p&&br&&/p&&p&&br&&/p&&p&&b&三、UIScrollView and UITableView&/b& &b& 的新特性&/b&&/p&&p&&br&&/p&&p&&b&1.Scroll Views&/b&&/p&&p&如果有一些文本位于UI滚动视图的内部,并包含在导航控制器中,现在一般navigationContollers会传入一个contentInset给其最顶层的viewController的scrollView,在iOS11中进行了一个很大的改变,不再通过scrollView的contentInset属性了,而是新增了一个属性:adjustedContentInset,下面的两张图的对比能够表示adjustContentInset表示的区域:&/p&&p&&br&&/p&&img src=&/50/v2-f6cd0e532b19d40c4ec6b2e7fbadaab3_b.png& data-rawwidth=&660& data-rawheight=&194& class=&origin_image zh-lightbox-thumb& width=&660& data-original=&/50/v2-f6cd0e532b19d40c4ec6b2e7fbadaab3_r.png&&&p&&br&&/p&&img src=&/50/v2-d4cda64fa893_b.png& data-rawwidth=&1041& data-rawheight=&123& class=&origin_image zh-lightbox-thumb& width=&1041& data-original=&/50/v2-d4cda64fa893_r.png&&&p&&br&&/p&&p&新增的contentInsetAdjustmentBehavior属性用来配置adjustedContentInset的行为,该结构体有以下几种类型:&/p&&p&&br&&/p&&img src=&/50/v2-44e8eef5de1bfe439b82287_b.png& data-rawwidth=&657& data-rawheight=&337& class=&origin_image zh-lightbox-thumb& width=&657& data-original=&/50/v2-44e8eef5de1bfe439b82287_r.png&&&p&&br&&/p&&p&&b&2. Table Views :在iOS 11中默认启用Self-Sizing&/b&&/p&&p&这个应该是UITableView最大的改变。我们知道在iOS8引入Self-Sizing 之后,我们可以通过实现estimatedRowHeight相关的属性来展示动态的内容,实现了estimatedRowHeight属性后,得到的初始contenSize是个估算值,是通过estimatedRowHeight x cell的个数得到的,并不是最终的contenSize,tableView就不会一次性计算所有的cell的高度了,只会计算当前屏幕能够显示的cell个数再加上几个,滑动时,tableView不停地得到新的cell,更新自己的contenSize,在滑到最后的时候,会得到正确的contenSize。在测试Demo中,创建tableView到显示出来的过程中,contentSize的计算过程如下图:&/p&&p&&br&&/p&&img src=&/50/v2-635bd50dde671a47eb6d8c4_b.png& data-rawwidth=&484& data-rawheight=&403& class=&origin_image zh-lightbox-thumb& width=&484& data-original=&/50/v2-635bd50dde671a47eb6d8c4_r.png&&&p&&br&&/p&&p&Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension:&/p&&p&&br&&/p&&img src=&/50/v2-165cca3cc7a744dd2a90_b.png& data-rawwidth=&656& data-rawheight=&64& class=&origin_image zh-lightbox-thumb& width=&656& data-original=&/50/v2-165cca3cc7a744dd2a90_r.png&&&p&&br&&/p&&p&如果目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,因为开启Self-Sizing之后,tableView是使用estimateRowHeight属性的,这样就会造成contentSize和contentOffset值的变化,如果是有动画是观察这两个属性的变化进行的,就会造成动画的异常,因为在估算行高机制下,contentSize的值是一点点地变化更新的,所有cell显示完后才是最终的contentSize值。因为不会缓存正确的行高,tableView reloadData的时候,会重新计算contentSize,就有可能会引起contentOffset的变化。iOS11下不想使用Self-Sizing的话,可以通过以下方式关闭:(前言中提到的问题也是通过这种方式解决的)&/p&&p&&br&&/p&&img src=&/50/v2-d255c1329fadca8fe00ebd_b.png& data-rawwidth=&658& data-rawheight=&112& class=&origin_image zh-lightbox-thumb& width=&658& data-original=&/50/v2-d255c1329fadca8fe00ebd_r.png&&&p&&br&&/p&&p&iOS11下,如果没有设置estimateRowHeight的值,也没有设置rowHeight的值,那contentSize计算初始值是 44 * cell的个数,如下图:rowHeight和estimateRowHeight都是默认值UITableViewAutomaticDimension 而rowNum = 15;则初始contentSize = 44 * 15 = 660;&/p&&p&&br&&/p&&img src=&/50/v2-15aa5bee679c0a17e711af_b.png& data-rawwidth=&800& data-rawheight=&183& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&/50/v2-15aa5bee679c0a17e711af_r.png&&&p&&br&&/p&&p&&b&3. Table Views:separatorInset 扩展&/b&&/p&&p&OS 7 引入separatorInset属性,用以设置 cell 的分割线边距,在 iOS 11 中对其进行了扩展。可以通过新增的UITableViewSeparatorInsetReference枚举类型的separatorInsetReference属性来设置separatorInset属性的参照值。&/p&&p&&br&&/p&&img src=&/50/v2-13e7f64bfeafbcd71897f8_b.png& data-rawwidth=&655& data-rawheight=&142& class=&origin_image zh-lightbox-thumb& width=&655& data-original=&/50/v2-13e7f64bfeafbcd71897f8_r.png&&&p&&br&&/p&&p&下图清晰的展示了这两种参照值的区别:&/p&&p&&br&&/p&&img src=&/50/v2-1e4dba116efe02e890a9e4_b.png& data-rawwidth=&435& data-rawheight=&611& class=&origin_image zh-lightbox-thumb& width=&435& data-original=&/50/v2-1e4dba116efe02e890a9e4_r.png&&&p&&br&&/p&&p&&b&4. Table Views 和 Safe Area&/b&&/p&&p&有以下几点需要注意:&/p&&p&● separatorInset 被自动地关联到 safe area insets,因此,默认情况下,表视图的整个内容避免了其根视图控制器的安全区域的插入。&/p&&p&● UITableviewCell 和 UITableViewHeaderFooterView的 content view 在安全区域内;因此你应该始终在 content view 中使用add-subviews操作。&/p&&p&● 所有的 headers 和 footers 都应该使用UITableViewHeaderFooterView,包括 table headers 和 footers、section headers 和 footers。&/p&&p&&br&&/p&&p&&b&5. 滑动操作(Swipe Actions)&/b&&/p&&p&在iOS8之后,苹果官方增加了UITableVIew的右滑操作接口,即新增了一个代理方法(tableView: editActionsForRowAtIndexPath:)和一个类(UITableViewRowAction),代理方法返回的是一个数组,我们可以在这个代理方法中定义所需要的操作按钮(删除、置顶等),这些按钮的类就是UITableViewRowAction。这个类只能定义按钮的显示文字、背景色、和按钮事件。并且返回数组的第一个元素在UITableViewCell的最右侧显示,最后一个元素在最左侧显示。从iOS 11开始有了一些改变,首先是可以给这些按钮添加图片了,然后是如果实现了以下两个iOS 11新增的代理方法,将会取代(tableView: editActionsForRowAtIndexPath:)代理方法:&/p&&p&&br&&/p&&img src=&/50/v2-0cfccd2ff1ae08aad0204_b.png& data-rawwidth=&657& data-rawheight=&142& class=&origin_image zh-lightbox-thumb& width=&657& data-original=&/50/v2-0cfccd2ff1ae08aad0204_r.png&&&p&&br&&/p&&p&这两个代理方法返回的是UISwipeActionsConfiguration类型的对象,创建该对象及赋值可看下面的代码片段:&/p&&p&&br&&/p&&img src=&/50/v2-837e3b44bdcc57aab3dc38b1eeb74676_b.png& data-rawwidth=&656& data-rawheight=&365& class=&origin_image zh-lightbox-thumb& width=&656& data-original=&/50/v2-837e3b44bdcc57aab3dc38b1eeb74676_r.png&&&p&&br&&/p&&p&创建UIContextualAction对象时,UIContextualActionStyle有两种类型,如果是置顶、已读等按钮就使用UIContextualActionStyleNormal类型,delete操作按钮可使用UIContextualActionStyleDestructive类型,当使用该类型时,如果是右滑操作,一直向右滑动某个cell,会直接执行删除操作,不用再点击删除按钮,这也是一个好玩的更新。&/p&&p&&br&&/p&&img src=&/50/v2-ccaf8b93b314_b.png& data-rawwidth=&655& data-rawheight=&141& class=&origin_image zh-lightbox-thumb& width=&655& data-original=&/50/v2-ccaf8b93b314_r.png&&&p&&br&&/p&&p&滑动操作这里还有一个需要注意的是,当cell高度较小时,会只显示image,不显示title,当cell高度够大时,会同时显示image和title。我写demo测试的时候,因为每个cell的高度都较小,所以只显示image,然后我增加cell的高度后,就可以同时显示image和title了。见下图对比:&/p&&p&&br&&/p&&img src=&/50/v2-3bdf910e65d11cfe24d64ddf4b42005b_b.png& data-rawwidth=&664& data-rawheight=&157& class=&origin_image zh-lightbox-thumb& width=&664& data-original=&/50/v2-3bdf910e65d11cfe24d64ddf4b42005b_r.png&&&p&&br&&/p&&p&&b&总结&/b&&/p&&p&大概介绍了iOS 11的UI方面的一些更新,大部分内容都用代码测试过了,有些更新确实是很实用,可以适配下iOS 11,有的更新可能会给现有APP造成bug,所以学习下这些内容还是很有必要的。&/p&&blockquote&&b&参考:&/b&&br&1、Updating Your App for iOS 11 - WWDC 2017 - Session 204 - iOS&br&2、iOS 8自动调整UITableView和UICollectionView布局&br&3、Mysteries of Auto Layout, Part 1 - WWDC2015&br&4、Mysteries of Auto Layout, Part 2 - WWDC2015&/blockquote&&p&&br&&/p&&p&&b&为了提高腾讯游戏的苹果审核通过率,腾讯专设了苹果审核测试团队,打造出iOS预审工具这款产品。经过两年的内部运营,腾讯内部应用的iOS审核通过率从平均35%提升到90%+。&/b&&/p&&p&&b&现今iOS预审工具对外开放,登录WeTest腾讯质量开放平台即可使用。&/b&&/p&&p&&b&体验地址:&a href=&/?target=http%3A///product/ios%3Ffrom%3Dcontent_zhihu& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&/product/ios&i class=&icon-external&&&/i&&/a& &/b&&/p&&p&&br&&/p&&p&&b&iOS预审服务&/b&&/p&&p&【扫描工具】上传IPA包、图片、视频、应用描述即可进行测试; 多维度自动扫描提审材料的被拒风险;1小时内反馈全面的扫描报告。&/p&&p&【专家预审】腾讯专家为您遍历App所有功能模块;全面暴露App内容被拒风险;跟进问题直至上线(需提供官方拒绝邮件)。&/p&&p&【专家咨询】资深预审专家一对一服务; 咨询时间灵活可选,按需购买;有的放矢解 决审核问题。&/p&&p&【ASO优化】专业团队多维度深度剖析App的ASO现状;围绕App目标用户群筛选高 度关联的关键词;帮助提升App在苹果应用商店中的曝光率。&/p&
商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接: WeTest 导读iOS 11 为整个生态系统的 UI 元素带来了一种更加大胆、动态的新风格。 本文介绍了iOS11在UI方面做了哪些更新,有些更新可以为用户…
&p&WWDC 2017 刚结束,虽然如预期的一样,缺少意料之外的惊喜,但依旧有不少新的特性和 API 值得圈点。抛开 Core ML 以及 ARKit 这些影响深远的亮点不谈,目前抢眼的系统升级,莫过于 UIKit 中新增的 Drag and Drop 特性了。&/p&
&h1&拖拽的意义&/h1&
&p&在阅读本文之前,建议读者先亲手把玩下 Drag and Drop 的各种姿势,有过实际的操作体验,才能更好的明白一些 API 设计背后的考量。&/p&
&p&现阶段只有 iPad 上能支持不同 App 之间的内容拖拽共享,iPhone 上只能在 App 内部拖拽内容,iPhone 上的这一限制使得 Drag and Drop 大打折扣,有可能是出于屏幕尺寸以及操作体验方面的考量。不过这还不是最终版本,后续 Apple 有可能会做出调整,毕竟拖拽带来的可能性太多了。&/p&
&p&Drag and Drop 允许不同 App 之间通过拖拽的方式共享内容,虽然 session video 中的演示(从相册中拖拽图片到 mail app)稍显简单,但这一新特性的想象空间远不止此,拖拽的操作方式开启了一个新的数据内容流动通道,内容的提供方和内容的消费方可以是不同 App,让 App 能更专注于自己擅长的领域,分工协作为用户提供更美妙的体验。&/p&
&p&比如,之前在微信聊天的时候,如果有一个不得不发的表情,用户只能先从搜狗输入法将图片保存的相册或剪切板,再通过额外的步骤输入到微信中,有了 Drag and Drop 之后,这一流程能简化到一步完成,就好像微信和搜狗输入是同一个 App 一样,在协同工作。&/p&
&p&不只是图片,广义上的内容涵盖,文本,链接,语音,图片,视频等等,不同内容组合在一起又能呈现不一样的形式。拖拽无论是在操作体验上,还是内容流通上都将把 iOS 系统的易用性带上一个新的台阶。&/p&
&p&下面我会结合一个实际的场景来介绍如何使用 Drag and Drop 特性。从数月前开始,我一直利用零碎的时间在开发一款个人 App:TKeyboard。TKeyboard 有一个很酷的特性,可以在 iPhone 上实时浏览 Mac 的文件系统,当我看到 Drag and Drop 时,一个脑洞应景而生。如果可以将 TKeyboard 中的图片直接拖拽到其他 App 中,那么你的 Mac 电脑就成了 iPhone 的一个备用存储,Mac 上的图片资源一步操作就能传递到其他 App 中,很美妙不是吗?先看下效果图:&/p&
&img src=&/v2-4a19a87e19a4d878b0d41a1c337e3ec9_b.jpg& data-rawwidth=&480& data-rawheight=&640& data-thumbnail=&/v2-4a19a87e19a4d878b0d41a1c337e3ec9_b.jpg& class=&origin_image zh-lightbox-thumb& width=&480& data-original=&/v2-4a19a87e19a4d878b0d41a1c337e3ec9_r.gif&&&p&使用新特性,新 API 也是个宝贵的学习过程,如果让你来设计这么一个看似简单,拓展性好,兼容性强的 Drag and Drop 功能,你会如何来实施呢?整个流程虽然谈不上复杂,但环节多,会稍显繁琐。我们来看看 Apple 的工程师是如何做的。&/p&
&p&Drag 与 Drop 可以分开来学习,因为你的 App 很可能只实现 Drag 或者 Drop 其中一项功能。&/p&
&h1&Drag&/h1&
&p&先看下最基础的场景,如何将 App 中的内容 Drag 起来。&/p&
&p&Drag 的对象是我们平时所接触的 UI 控件,UILabel,UIImageView,或者自定义的 View。让控件可拖动,只需要给控件添加 UIDragInteraction 对象:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//EFinderTodayCell.h
@interface EFinderTodayCell : UIView
//EFinderTodayCell.m
- (void)enableDrag
if (IOS11) {
UIDragInteraction* drag = [[UIDragInteraction alloc] initWithDelegate:self];
[self addInteraction:drag];
self.userInteractionEnabled =
&/code&&/pre&&/div&
&p&EFinderTodayCell 作为 UIView 的子类,在添加 UIDragInteraction 对象之后,就具备了可被 Drag 的行为,接下来 Drag 的交互控制都通过 UIDragInteractionDelegate 来实现。&/p&
&p&UIDragInteractionDelegate 中提供了不少方法,可以对 Drag 的行为做不同程度的定制,一个个看:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (NSArray&UIDragItem *& *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id&UIDragSession&)session
NSArray* items = [self itemsForSession:session];
&/code&&/pre&&/div&
&p&单指长按某个 View 时,如果添加了 UIDragInteraction,Drag 即刻启动,进入 itemsForBeginningSession 的回调,这个方法中出现的三个类,关系也十分简单。一个 UIDragInteraction 可以包含多个 UIDragSession,每个 UIDragSession 又可以包含多个 UIDragItem。UIDragItem 则是 Drop 时接收方所受到的对象。&/p&
&p&我们可以给一个 UI 元素安装多个 UIDragInteraction,通过设置 enabled 属性来决定启用哪一个 UIDragInteraction,手指 A 长按 UI 元素的时候,启用的 UIDragInteraction 对象会生成一个 UIDragSession 对象,如果手指不松开,另一个手指 B 重新长按另一个 UI 元素,则会建立一个新的 UIDragSession,手指 B 如果点击另一个 UI 元素,则会添加一个新的 UIDragItem。理清三者的关系是深度定制 Drag 的前提,可以用下图表示:&/p&
&img src=&/v2-98ae233a0a2af1cf5b0f_b.png& data-rawwidth=&1384& data-rawheight=&680& class=&origin_image zh-lightbox-thumb& width=&1384& data-original=&/v2-98ae233a0a2af1cf5b0f_r.png&&&p&如何生成 UIDragItem 呢?这里又需要几个新对象:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (NSArray*)itemsForSession:(id&UIDragSession&)session
NSItemProvider* provider = [[NSItemProvider alloc] initWithObject:_item];
UIDragItem* item = [[UIDragItem alloc] initWithItemProvider:provider];
item.localObject = _
return @[item];
&/code&&/pre&&/div&
&p&UIDragItem 包含一个 NSItemProvider 对象,NSItemProvider 对象则包含一个 id\ 对象。protocol NSItemProviderWriting 则定义了 UIDragItem 中所包含的数据最后以何种形式提供个 Drop 方。我们看一个样例 model 类如何实现 NSItemProviderWriting:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//TFinderItem.h
@interface TFinderItem : NSObject &NSItemProviderWriting&
//TFinderItem.m
#pragma mark- NSItemProviderWriting
- (NSArray&NSString *&*)writableTypeIdentifiersForItemProvider
return @[@&public.jpeg&, @&public.png&];
- (nullable NSProgress *) loadDataWithTypeIdentifier:(nonnull NSString *)typeIdentifier forItemProviderCompletionHandler:(nonnull void (^)(NSData * _Nullable, NSError * _Nullable))completionHandler {
//发起网络请求,获取数据...
self.providerCompleteBlock = completionH
return [NSProgress new];
&/code&&/pre&&/div&
&p&writableTypeIdentifiersForItemProvider 返回 UIDragItem 所提供的 UTI,数据的接收方通过 UTI 知道我们所传递的数据格式。&/p&
&p&数据从 Server 获取回来之后,通过 completionHandler 以 NSData 形式传递即可。Drop 的实现者通过 UTI 和 UIDragItem 中的 NSData 即可取出自己感兴趣的数据,UIDragItem 的组成可以用下图表示:&/p&
&img src=&/v2-202cb95e44d5b3658dd0_b.png& data-rawwidth=&1408& data-rawheight=&432& class=&origin_image zh-lightbox-thumb& width=&1408& data-original=&/v2-202cb95e44d5b3658dd0_r.png&&&p&从上面两张图就能看出 Drag and Drop 的大致设计思路,这些类之间是以类似 tree 的关系组合在一起,对象虽多,但结构清晰。理解了这些关键类之间的关系,再看 UIDragInteractionDelegate 中的各个回调方法,各自在什么场景下触发就了然于胸了。&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//某个 UI 元素安装了 UIDragInteraction,单指长按时生成 UIDragSession,进入回调,索取 UIDragItem。
- (NSArray&UIDragItem *& *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id&UIDragSession&)session
&/code&&/pre&&/div&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//手指 A 长按某个 UI 元素后,手指 B 单击另外的 UI 元素,进入回调,允许添加更多的 UIDragItem 到当前 UIDragSession 中。
- (NSArray&UIDragItem *& *)dragInteraction:(UIDragInteraction *)interaction itemsForAddingToSession:(id&UIDragSession&)session withTouchAtPoint:(CGPoint)point
&/code&&/pre&&/div&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//有另外的 UIDragItem 通过单击加入到 UIDragSession 中,通知其他 UIDragInteractionDelegate
- (void)dragInteraction:(UIDragInteraction *)interaction session:(id&UIDragSession&)session willAddItems:(NSArray&UIDragItem *& *)items forInteraction:(UIDragInteraction *)addingInteraction
&/code&&/pre&&/div&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//单指长按某个 UI 元素,Drag 开始,生成新的 UIDragSession,进入回调
- (void)dragInteraction:(UIDragInteraction *)interaction sessionWillBegin:(id&UIDragSession&)session
&/code&&/pre&&/div&
&p&其他回调就不一一列举了。&/p&
&p&Drag 另一个重要的定制是对拖动的 UI 元素生成 Preview,并在不同的阶段改变 Preview 的形态。&/p&
&p&当单指长按 UI 元素时,元素会被举起(Lift),Lift 动画由系统自动生成,但需要我们通过如下方法来提供 Preview:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (nullable UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id&UIDragSession&)session
UIDragPreviewParameters* params = [UIDragPreviewParameters new];
params.backgroundColor = [UIColor clearColor];
UITargetedDragPreview* preview = [[UITargetedDragPreview alloc] initWithView:_iconView parameters:params];
&/code&&/pre&&/div&
&p&系统索取的是一个 UITargetedDragPreview 对象,UITargetedDragPreview 则由 UIView 的子类和 UIDragPreviewParameters 构成。UIDragPreviewParameters 可以设置 Preview 的展示参数,比如 backgroundColor 和 visiblePath。&/p&
&p&visiblePath 是另一个重要的参数,它实际是一个 UIBezierPath 对象,可以给 Preview 添加特定形状的 mask,比如可以通过如下代码设置圆角:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&UIDragPreviewParameters* params = [UIDragPreviewParameters new];
UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:imgView.bounds cornerRadius:5];
params.visiblePath =
&/code&&/pre&&/div&
&p&这里值得一提 UIDragPreview 和 UITargetedDragPreview 之间的差别。UIDragPreview init 方法中传入的 View 必须存在于活跃 Window 上,否则 Preview 会展示空:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// view 必须存在活跃的 superView
- (instancetype)initWithView:(UIView *)view parameters:(UIDragPreviewParameters *)parameters
&/code&&/pre&&/div&
&p&UITargetedDragPreview 中传入的 View 无此要求,不过我们需要提供另一个 UIDragPreviewTarget 对象,来告诉 UITargetedDragPreview 在哪个 superView 和位置上展示 Preview,类似:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//Container 和 Center 分别指定 superView 和 位置
UIDragPreviewTarget* target = [[UIDragPreviewTarget alloc] initWithContainer:_iconView.superview center:_iconView.center];
UITargetedDragPreview* preview = [[UITargetedDragPreview alloc] initWithView:imgView parameters:params target:target];
&/code&&/pre&&/div&
&p&另外还有一些 Drag 不同阶段的回调,允许我们对被拖动的 UI 元素做动画:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//Drag 发生时,将被拖动的图片透明度改为 0.5
- (void)dragInteraction:(UIDragInteraction *)interaction willAnimateLiftWithAnimator:(id&UIDragAnimating&)animator session:(id&UIDragSession&)session
[animator addAnimations:^{
_iconView.alpha = 0.5;
//Drag 完成后,将被拖动的图片透明度改为 1.0
- (void)dragInteraction:(UIDragInteraction *)interaction item:(UIDragItem *)item willAnimateCancelWithAnimator:(id&UIDragAnimating&)animator
[animator addAnimations:^{
_iconView.alpha = 1.0;
//Drag 取消后,将被拖动的图片透明度改为 1.0
- (void)dragInteraction:(UIDragInteraction *)interaction session:(id&UIDragSession&)session didEndWithOperation:(UIDropOperation)operation
_iconView.alpha = 1.0;
&/code&&/pre&&/div&
&h1&Drop&/h1&
&p&Drop 则可以看做是 Drag 的逆向过程,将 Drag 传递过来的 UIDragItem 解析后,取出自己感兴趣的数据。Drop 流程所涉及到的对象,几乎都是和 Drag 相对应的,理解了 Drag,再看 Drop 很好理解。&/p&
&p&我们可以向目标 UI 元素添加 UIDropInteraction,使其具备接收来自 Drag 数据的能力:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (void)enableDrop
if (IOS11) {
if (@available(iOS 11.0, *)) {
UIDropInteraction* drop = [[UIDropInteraction alloc] initWithDelegate:self];
[self addInteraction:drop];
&/code&&/pre&&/div&
&p&之后 Drop 的行为都交由 UIDropInteractionDelegate 来控制。&/p&
&p&第一步先询问 delegate 是否可以处理来自于 Drag 的数据:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id&UIDropSession&)session
if (session.localDragSession != nil) { //ignore drag session started within app
BOOL canHandle =
canHandle = [session canLoadObjectsOfClass:[UIImage class]];
return canH
&/code&&/pre&&/div&
&p&如果我们想忽略来自于 App 内部的 Drag,可以通过 localDragSession 这一属性判断,如果是来自于外部 App 的 Drag,localDragSession 为 nil。&/p&
&p&UIDropSession 是由系统封装好的对象,canLoadObjectsOfClass 可以让我们判断来自于 Drag 的数据里,是否有我们感兴趣的类型。这是第一次系统向我们询问是否对于 Drag 中的数据感兴趣。&/p&
&p&第二次且最后一次机会告知系统,是否能消化 Drag 中的数据:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id&UIDropSession&)session
if (@available(iOS 11.0, *)) {
return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy];
&/code&&/pre&&/div&
&p&如果此时发现 session 中的数据无法接收,可以返回 UIDropOperationCancel。&/p&
&p&前面两步通过之后,接下来是从 Session 中取出来自于 Drag 的数据:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id&UIDropSession&)session
[session loadObjectsOfClass:[UIImage class] completion:^(NSArray&__kindof id&NSItemProviderReading&& * _Nonnull objects) {
for (id object in objects) {
UIImage* image = (UIImage*)
if (image) {
//handle image
&/code&&/pre&&/div&
&p&performDrop 中的操作最好是采用异步的方式,任何费时的操作都会导致主线程的卡顿,一旦时间过长,会被系统 watchdog 感知并 kill 掉。UIDropSession 所提供的 loadObjectsOfClass 回调会发生在工作线程,所以在 completion block 中如果有涉及 UI 的操作,记得切回主线程。&/p&
&p&只需前面三个回调,即可接收来自于 Drag 中的图片数据。比如从系统相册 Drag 照片,在 performDrop 回调里就能取得 UIImage 对象。&/p&
&p&另外需要注意用户 Drag 时,只要不松开手指,可以持续进入以下三个回调:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//Drag 的 UI 元素进入 Drop 的区域
- (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnter:(id&UIDropSession&)
//Drag 的 UI 元素在 Drop 区域内反复移动,多次进入
- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id&UIDropSession&)session
//Drag 的 UI 元素离开 Drop 的区域
- (void)dropInteraction:(UIDropInteraction *)interaction sessionDidExit:(id&UIDropSession&)
&/code&&/pre&&/div&
&p&我们虽然无法在用户 Drag 时,改变 Drag 的 preview,但用户一旦松开手指,执行 drop 时,UIDropInteractionDelegate 中的以下两个回调可以让我们对 drop 的动画效果做一定程度的定制:&/p&
&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&//手指松开,控制 Drag Preview 如何自然的过渡到 Drop 之后的 Preview
- (nullable UITargetedDragPreview *)dropInteraction:(UIDropInteraction *)interaction previewForDroppingItem:(UIDragItem *)item withDefault:(UITargetedDragPreview *)defaultP
//手指松开,Drop 时,控制 Drop 区域的其他 UI 元素如何展示动画
- (void)dropInteraction:(UIDropInteraction *)interaction item:(UIDragItem *)item willAnimateDropWithAnimator:(id&UIDragAnimating&)
&/code&&/pre&&/div&
&p&这也是 Drop 体验要做好真正复杂的部分,来自于 Drag 的数据可能是存在于网络的,用户 Drop 之后,提供 Drag 的 App 此时可能需要从网络上获取真正需要传输的数据,这是一个异步的过程,提供 Drop 功能的 App 需要竭尽所能,通过巧妙的动画或者 UI 交互设计,让用户愿意等待这一“漫长”过程,而且能顺畅自然的感知 Drag 的数据是如何过渡到 Drop 区域的。还需要处理各种异常场景,比如用户不愿继续等待选择取消 Drop,比如 Drag 一方由于内部异常最终无法提供数据,比如最终抵达的数据超过 App 能承受的范围(Image 尺寸过大,Text 过长)等等,每一个场景都需要动画交互。所以这里才是功夫所在。&/p&
&p&Drag and Drop 的关键 API 并不多,十个手指头差不多能数过来,如何用好这些 API,如何把体验做精细,如何把 Drag and Drop 中蕴含的更多可能性发掘出来,需要行业里的开发者们一起努力探索,毫不夸张的说,有时候一个新特性就能支撑一个新 App。&/p&
&h1&总结&/h1&
&p&Drag and Drop 是一种体验上的创新,对于 iPad 这种大屏设备,多手指同时工作可以完成更复杂且实用的操作。我用右手 Drag 图片之后,左手继续在 iPad 上操作其他 App 完全不受影响,苹果在 multi-touch 的体验上应该是下足了功夫。iOS 11 针对 iPad 的优化,以及新款 iPad Pro 的各种硬件升级,可以看出苹果对于未来 iPad 销量增长寄以厚望。手机和 PC 之间的市场争夺战已日趋于平稳,iPad 或许是进一步蚕食 PC 市场份额的另一项利器,但如何在触摸屏上,把交互和体验做到 PC 一般自然舒畅还是项任重道远的任务,WWDC 2017 或许是个新的起点。&/p&
&p&推荐观看:&/p&
&p&WWDC 2017 | Session 203&/p&
&p&WWDC 2017 | Session 213&/p&
&p&WWDC 2017 | Session 223&/p&
&p&WWDC 2017 | Session 227&/p&
&a href=&/?target=http%3A//Drag%2520and%2520Drop%20Apple%2520Developer%2520Documentation& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Drag and Drop 开发者官方文档&i class=&icon-external&&&/i&&/a&
&p&Drag and Drop 是个好 “API“,希望 iPhone 也能有。&/p&
&p&本文已适配 iOS 11。&/p&
WWDC 2017 刚结束,虽然如预期的一样,缺少意料之外的惊喜,但依旧有不少新的特性和 API 值得圈点。抛开 Core ML 以及 ARKit 这些影响深远的亮点不谈,目前抢眼的系统升级,莫过于 UIKit 中新增的 Drag and Drop 特性了。
拖拽的意义
在阅读本文之前,建议…
&p&本周 Swift 创始人 Chris Lattner 突然在 Twitter 宣布离开特斯拉,这离他加入还没多久,特斯拉也回应说双方不适合,这应该算是招聘事故了。苹果下架了一些应用,媒体又开始炒作,苹果在国内的舆论环境很不利啊。&/p&&h2&新闻&/h2&&a href=&/?target=http%3A//.cn/mobile/n/n//doc-ifyhmtcf2797339.shtml& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&苹果回应清除 App 事件: 为了生态健康半年下架 10 万应用&i class=&icon-external&&&/i&&/a&&p&在此次回应中,苹果谈到下线应用的原因是对 App Store 做了一次“清理”,而非之前国内报道仅针对热更新应用下架。本次下架主要针对:对山寨克隆应用、传播盗版音乐内容、多年无人下载的应用、不兼容 64 应用系统、以及有安全隐患的热更新应用等这 5 种应用进行清理。&/p&&a href=&/?target=http%3A///articles/soft/620743.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&谷歌官方确认:Android 8.0 系统正式推送&i class=&icon-external&&&/i&&/a&&p&谷歌为参与 Android Beta 的用户分发了全新的 Android O 系统,也就是第三个开发者预览版。系统标签是 OPP3.,支持 Nexus 5X, Nexus 6P, Pixel, Pixel XL, Pixel C、Nexus Player 等在内的设备,目前,工厂镜像也已经可以下载。&/p&&h2&教程&/h2&&p&&strong&iOS&/strong&&/p&&a href=&/?target=http%3A//limboy.me/tech//the-right-way-to-ios-architecture.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The Right Way to Architect iOS App with Swift&i class=&icon-external&&&/i&&/a&&p&关于 iOS 架构的文章感觉已经泛滥了,前一阵正好 Android 官方推了一套 &a href=&/?target=https%3A///topic/libraries/architecture/guide.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&App Architecture&i class=&icon-external&&&/i&&/a& 。作者借此探索了怎样的架构才适合 iOS 。你可以从本文中了解到为什么要有架构、作者探索的过程、前端和 Android 技术栈的架构。&/p&&a href=&/?target=http%3A//blog.csdn.net/wsxzk123/article/details/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ImageIO --- 完成渐进加载图片&i class=&icon-external&&&/i&&/a&&p&加载一张图片时,特别是加载一张非常大的图片时,选择渐进加载图片时,用户体验会好上很多,本文介绍了如何使用 ImageIO 实现渐进加载功能。&/p&&a href=&/?target=http%3A///p/bff94aa90836& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vue 全家桶 + Electron 开发的一个跨三端的应用 &i class=&icon-external&&&/i&&/a&&p&本文介绍了利用 Vue.js 实现 objc 中国 的跨平台全栈应用,支持桌面应用、Web 应用、手机 App。&/p&&a href=&/?target=https%3A//news.realm.io/cn/news/marius-rackwitz-challenges-building-swift-framework/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&构建 Swift 框架所面临的挑战&i class=&icon-external&&&/i&&/a&&p&在 Realm 首次发布之前,Apple 就推

我要回帖

更多关于 字节对齐 的文章

 

随机推荐