本文共 6194 字,大约阅读时间需要 20 分钟。
iOS Views要点
UIView的safe area
在iOS11中,UIViewController的topLayoutGuide
和bottomLayoutGuide
被UIView
的safe area替代了
@available(iOS 11.0, *)open var safeAreaInsets: UIEdgeInsets { get }@available(iOS 11.0, *)open var safeAreaLayoutGuide: UILayoutGuide { get }
,其含义是The layout guide representing the portion of your view that is unobscured by bars and other content
表示不被bar和其它content这比的部分
其解释可参考:
如下的扩展:
extension UIView { var sa_safeAreaInsets: UIEdgeInsets { if #available(iOS 11, *) { return safeAreaInsets } return UIEdgeInsets.zero } var sa_safeAreaFrame: CGRect { if #available(iOS 11, *) { return safeAreaLayoutGuide.layoutFrame } return bounds }}
如下所示:
可见现在的iPhone X在竖屏有top和bottom,在横屏的时候有Left、right和bottom的inset
- (void)viewWillLayoutSubviews{ [super viewWillLayoutSubviews]; NSLog(@"%f, %f, %f, %f", self.view.safeAreaInsets.top, self.view.safeAreaInsets.right,self.view.safeAreaInsets.bottom,self.view.safeAreaInsets.left );}
在竖屏的safeAreaInsets
输出为:44.000000, 0.000000, 34.000000, 0.000000
safeAreaInsets
输出为:0.000000, 44.000000, 21.000000, 44.000000
Subview与SuperView
view层级的一些效果:
- view从其superview移除,那么view的subview也将移除
view的透明度会被subview继承
- view的alpha属性会影响其子视图的透明性。如果一个superview的alpha值为0.5,那么它的所有子视图透明度都不会有超过0.5,因为无论它们的透明度为多少,都会相对0.5而绘制
view可以限制其subview的绘制,使它们在view外的任何部分都不显示。 这被称为clipping,可通过
clipsToBounds
属性设置
view的动态变化会有事件通知,可继承view,重写一些下面的方法:
willRemoveSubview(_:)
,didAddSubview(_:)
willMove(toSuperview:)
,didMoveToSuperview
willMove(toWindow:)
,didMoveToWindow
Frame与Bounds
1.修改bounds的size
属性,会影响到其frame
,但是view的center
属性却不受其影响
如下的例子:
let v1 = UIView(frame:CGRect(x: 113, y: 111, width: 132, height: 194)) v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1) let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10)) v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1) mainview?.addSubview(v1) v1.addSubview(v2)
当前显示效果为:
如果我们修改子view的bounds的size
属性,如下:
v2.bounds.size.height += 20 v2.bounds.size.width += 20
会发现,view的frame
有调整,但center
却还在是原来的位置。此时子view完全覆盖父view,但view的center并没有改变
2.如果改变view的bounds
的origin
属性,则会移动其自身坐标系原点的位置
还是上面的例子,移动v1
的origin
:
v1.bounds.origin.x += 10 v1.bounds.origin.y += 10
移动后,效果如下,会发现改变父view的orgin,会影响子view的位置
如何理解这种改变呢?可以这样理解,v1的origin
原来叫(0.0,0.0)
,现在把它改名了叫做(10.0,10.0)
,而此时v2(frame为(10.0, 10.0, 112.0, 174.0)
)的frame的origin
刚好为(10.0,10.0)
,所以它们重合了
在raywenderlich
上有视频,利用改变父view的origin
来实现一个自定义的scrollView
import UIKitclass RWScrollView: UIView { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder); //添加拖动手势 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panView(with:))) addGestureRecognizer(panGesture) } //原理是:改变父view的origin的位置,会改变子view的位置 //注意添加@objc @objc func panView(with gestureRecognizer: UIPanGestureRecognizer) { let translation = gestureRecognizer.translation(in: self); print("translation.y: \(translation.y)") //动画 UIView.animate(withDuration: 0.20) { self.bounds.origin.y = self.bounds.origin.y - translation.y } //reset 平移 gestureRecognizer.setTranslation(CGPoint.zero, in: self) }}
效果如下:
屏幕坐标
在iOS7和它之前的版本中,screen的坐标是不变的。而在iOS8之后则有了新的变化,screen的坐标会随着app旋转而改变。
参考:
在iOS 8上UIScreen
提供了两个新的属性,这连个属性都遵守UICoordinateSpace
协议:
coordinateSpace
这个坐标的空间会旋转,它的原点总是位于左上角fixedCoordinateSpace
这个坐标的空间是不变的,是设备的左上角(相对于home键,这里应该是指portrait状态的设备,屏幕的左上角)
View Programming Guide for iOS
深入了解UIView,记录下中相关内容。
UIView的事件响应顺序
When a touch occurs inside a specific view, the system sends an event object with the touch information directly to that view for handling. However, if the view does not handle a particular touch event, it can pass the event object along to its superview. If the superview does not handle the event, it passes the event object to its superview, and so on up the responder chain. Specific views can also pass the event object to an intervening responder object, such as a view controller. If no object handles the event, it eventually reaches the application object, which generally discards it.
Content Modes
Each view has a content mode that controls how the view recycles its content in response to changes in the view’s geometry(几何结构) and whether it recycles its content at all. When a view is first displayed, it renders its content as usual and the results are captured in an underlying(潜在的) bitmap. After that, changes to the view’s geometry do not always cause the bitmap to be recreated. Instead, the value in thecontentMode
property determines whether the bitmap should be scaled to fit the new bounds or simply pinned to one corner or edge of the view. 如下的动作会导致content mode被应用:
- Change the width or height of the view’s
frame
orbounds
rectangles. - Assign a transform that includes a scaling factor to the view’s
transform
property.
默认的view的frame超出superview的部分是不会被裁剪的,把clipsToBounds
设为YES
可以达到裁剪的结果。裁剪之后,发生在view被裁剪区的触摸事件不会被deliver到view上。
setNeedsLayout
vs layoutIfNeeded
参考
system会标记需要redrawn的view,在update cycle中来redrawing
setNeedsLayout
告知system你想要layout并redraw所有的view及其子view,在update cycle的时候。这是一个异步的activity,你不知道update cycle什么时候会到来。
layoutIfNeeded
是一个同步的过程,告知system你想要立即layout和redraw这个view及其子view,需要立即做,不要等待update cycle。
所以layoutIfNeeded
会立即更新,而setNeedsLayout
等待至下一个update cycle
View运行时交互模型(The Runtime Interaction Model for Views)
- user触摸屏幕
- 硬件把touch event报告给UIKit
- UIKit把touch包装成一
UIEvent
对象,并把它分派到对应的view。 view的事件处理code来响应event。例如,你的代码可能如下:
- 改变view或者其subview的属性(frame, bounds, alpha等)
- 调用
setNeedsLayout
方法,把view(或者其subview)mark为需要更新布局 - 调用
setNeedsDisplay
或者setNeedsDisplayInRect:
方法,把view(或者其subview)mark为需要被重绘 - 通知controller改变部分数据
如果view的geometry有任何的改变,UIKit按照以下的规则来更新它的subviews:
a. 如果你配置了autoresizing,UIkit根据这些规则来调整每个view b.如果view实现了layoutSubviews
方法,UIKit会调用这个方法如果view的一部分被mark为需要被redrawn,UIKit会要求view重绘它自身
- 更新后的view会与应用的其它可见的content合成,然后给硬件来显示
- 硬件把渲染后的内容显示在屏幕上
Windows
一个Window对象有如下的功能:
- It contains your application’s visible content.
- It plays a key role in the delivery of touch events to your views and other application objects.
- It works with your application’s view controllers to facilitate orientation changes.
转载地址:https://windzen.blog.csdn.net/article/details/52702283 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!