本章节主要从视图、网络、设计模式几个方面考察开发者的开发水准,这是任何一个合格的 iOS 开发者都应该具备的基本素养。
iOS 开发中最重要的 API 就是 UIKit。它是苹果官方提供的管理界面和交互的最基本的 API。UIKit 被用在所有的 iPhone 和 iPad 开发中,它涵盖的内容包括触摸和交互处理、视图布局、图形绘制中。可以说 UIKit 相关知识点的考察是所有面试中最基本、最必不可少、最重要的一环。
本节将从用户界面聊起,回答开发中常见的布局和交互问题;之后将重点集中在动画渲染上,最后的问答题将集中在 iPad 的多屏开发上。对于 iOS 11 中最新的 drag and drop 和安全区域亦有涉及。
关键词:#storyboard #xib #Frame #Auto Layout
这道题本身问法十分模糊。定义一个 Label,指的是创建一个,还是说给它做相应的布局,亦或是设置它的属性值?这都是要和面试官进行进一步沟通确定的。
假如我们要从零创建一个 label,配置它在页面上的布局,并设置属性值,有以下几种方式。
关键词:#可视化 #多人协作 #性能
storyboard/xib 的开发方式优点和缺点都十分明显。优点是:
缺点是:
关键词: #性能
加分回答:
解决方法是尽量压缩视图层级减少计算量;同时 Layout 的计算也可以通过后台线程来处理,这样就可以不阻塞主线程操作。计算结果亦可以缓存起来,加速之后界面布局渲染。成熟的解决方案有 Facebook 的 ComponentKit,Pinterest 的 Texture(前身是 ASDK ),以及 LinkdedIn 的 LayoutKit。
关键词: #性能 #交互
关键词: #坐标 #父视图
介绍完上述概念后,下面用画图的方式来讲解这三个词的区别。如下图:
其中 View B 左上角的点的frame 值是(200, 100),bounds 值是(0, 0),center 所对应点的值是
(275, 200)。
关键词: #布局 #周期
关键词: #安全区域
由于 iPhone X 全新的刘海设计,iOS 11 中引入了安全区域(Safe Area)这套概念。如下图:
关键词: #UIViewPropertyAnimator #UIView Animation #CALayer Animation
最主要的实现动画方式有 3 种,UIView Animation、CALayer Animation、UIViewPropertyAnimator。
+ animateWithDuration:animations:
,其中持续时间(duration)为基本参数,block 中对 UIView 属性的调整就是动画结束后的最终效果。除此之外他还有关键帧动画和两个 view 转化等接口。它实现的动画无法回撤、暂停、与手势交互。关键词: #UIViewPropertyAnimator #交互式动画
这道题很明显是要求实现动画。然而,题目中对于动画的各种参数(持续时间,延时,速度控制等)都没有要求。我们在做这道题目的时候一定要就相关细节向面试官询问清楚,切忌上来就写——实际开发中最怕用户需求都不明白就写代码,最终也只会是南辕北辙。
假设圆形小球已经在屏幕上,面试官没有参数要求,只是要实现水平移动的效果。那么实现代码如下:
// UIViewPropertyAnimator实现
let animator = UIViewPropertyAnimator(duration: 2, curve: .linear) {
circle.frame = circle.frame.offsetBy(dx: 200, dy: 0)
}
animator.startAnimation()
// UIView Animation实现
UIView.animate(withDuration: 2) {
circle.frame = circle.frame.offsetBy(dx: 200, dy: 0)
}
// CALayer实现
let animation = CABasicAnimation.init(keyPath: "position.x")
animation.fromValue = circle.center.x
animation.toValue = circle.center.x + 200
animation.duration = 2
self.circle.layer.add(animation, forKey: nil)
追问:假如需要根据手势来控制小球的水平移动,该怎么操作?
这次考察的是交互式动画,那么交互式动画用 UIViewPropertyAnimator 来做最为方便。关于手势具体如何控制球的移动,请向面试官询问。我们假设面试官给出如下要求:
var progress: CGFloat = 0
var animator: UIViewPropertyAnimator!
override func viewDidLoad() {
let gesture = UIPanGestureRecognizer(target: self, action: Selector.handlePan)
view.addGestureRecognizer(gesture)
animator = UIViewPropertyAnimator.init(duration: 2, curve: .easeOut, animations: {
self.circle.frame = self.circle.frame.offsetBy(dx: 200, dy: 0)
})
}
func handlePan(recognizer:UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
animator.pauseAnimation()
progress = animator.fractionComplete
case .changed:
let translation = recognizer.translation(in: self.circle)
animator.fractionComplete = translation.x / 200 + progress
case .ended:
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
default:
break
}
关键词:#Adaptive UI #Size Class #Auto Layout
为了针对各种机型,苹果在 iOS 8 中引入了 Adaptive UI 的概念。所以要保证应用的 UI 在各种情况下依然良好,主要注意以下几个点:
关键词:#Drag and Drop
这道题考察的是 iOS 11 最新引入的 Drag and Drop 功能。跟很多面试题一样,它没有说明起始和终止的 UIImageView 是否在一个应用之内。如果在同一个应用之内,那么无论是 iPhone 还是 iPad 都能实现这样的功能;如果是把图片从一个应用拖拽到另一个应用之上,那么只能是 iPad 实现。
我们假设面试官考察的是在同一个应用中,将一张图片从一个 UIImageView 中拖拽到另一个 UIImageView 。
Drag and Drop 一般实现起来分3步:
1. 对相应的 UIImageView 分别添加 drag 和 drop delegate
dragImageView.addInteraction(UIDragInteraction(delegate: self))
dropImageView.addInteraction(UIDropInteraction(delegate: self))
注意,dragImageView 和 dropImageView 的 userInteractionEnabled 必须是 true。
2.实现 drag delegate 规定的方法
extension ViewController: UIDragInteractionDelegate {
func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
if interaction.view == dragImageView {
let dragImage = dragImageView.image
let itemProvider = NSItemProvider(object: dragImage!)
let dragItem = UIDragItem(itemProvider: itemProvider)
return [dragItem]
} else {
return []
}
}
}
这个方法的功能就是告诉系统,我们要拖动的对象。
方法里面的 NSItemProvider 简单来说就是用来在 Drag and Drop,或者 Extension app 和 Host app 之间传输数据的类。
UIDragItem 则是像对 NSItemProvider 的进一步封装,除了包含传输数据外,还可以自定义一些数据。
实现完该方法后,图片就可以从 dragImageView 里拖动出来了。
3.实现 drop delegate 对应的方法
一般来讲,需要实现 3 个方法:
// 询问是否可以处理 drag 的数据,默认是 true,所以并不一定要实现
func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool
// 询问系统当 drop 之时,以何种方式处理 drag 的数据
// UIDropProposal对应的操作是 cancel, forbidden, copy, move
func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal
// drop 已经发生,取出 drag 中的数据并进行处理
func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession)
具体的代码如下:
func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
let dropLocation = session.location(in: view)
let operation: UIDropOperation
if dropImageView.frame.contains(dropLocation) {
operation = .copy
} else {
operation = .cancel
}
return UIDropProposal(operation: operation)
}
func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
session.loadObjects(ofClass: UIImage.self) { [weak self] (imageItems) in
self?.dragImageView.image = nil
self?.dropImageView.image = imageItems.first as? UIImage
}
以上是最简单直接的实现方法。Drag and Drop 中还有很多可以定制的方法和属性,例如支持多点触摸的 preview 方法。工作中,你可能只需要实现 drag 功能,也可能只需要支持 drop 功能。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。