TOC
前言
今天要讨论的是一个平常不那么常用的方法 func exchangeSubview(at index1: Int, withSubviewAt index2: Int)
的踩坑记。
我们先看一下官方文档中的描述:
Exchanges the subviews at the specified indices.
Each index represents the position of the corresponding view in the array in the
subviews
property. Subview indices start at 0 and cannot be greater than the number of subviews. This method does not change the superview of either view but simply swaps their positions in thesubviews
array.
简单来讲就是从一个 view
中交换两个 subview
的位置,先从 subviews
数组里获取两个 subview
的 index
,然后从 subviews
数组对象里面交换各自的位置重新渲染。从描述里我们能大概猜想到如何使用此方法,上代码:
let view1 = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 200))
view.addSubview(view1)
let view2 = UIView(frame: CGRect(x: 70, y: 120, width: 200, height: 200))
view.addSubview(view2)
/// 请忽略这里的强转,真实场景里要避免强转
let view1Index = view.subviews.firstIndex(of: view1)! // index为0
let view2Index = view.subviews.firstIndex(of: view2)! // index为1
view.exchangeSubview(at: view1Index, withSubviewAt: view2Index)
在大部分场景下如上代码会按照文档里的描述一样正常工作。
踩坑
但是今天我在实际使用过程当中发现并非如此,继续上代码:
let view1 = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 200))
view.addSubview(view1)
let view2 = UIView(frame: CGRect(x: 70, y: 120, width: 200, height: 200))
view.addSubview(view2)
/// 这里是新增的代码部分
let layer1 = CALayer()
view.layer.insertSublayer(layer1, at: 0)
let view1Index = view.subviews.firstIndex(of: view1)! // index还是为0
let view2Index = view.subviews.firstIndex(of: view2)! // index还是为1
view.exchangeSubview(at: view1Index, withSubviewAt: view2Index)
运行代码就会发现交换的并不是预期的两个 view
,而是交换成一个莫名其妙不知哪里来的 view
,如果打印 subviews
发现 index
也是对的。那么 index
也是系统给的,方法也是调用的正确的,为何结果就不对了呢?
如果重新看一下上述代码就会发现跟之前唯一的差别就是新插入了一个纯layer,问题也是出现在这里。当给一个 view
的 layer
插入一个 layer
的时候它会加到 sublayers
里面,而非 subviews
。也就是说如果你的 view
里即使用了 addSubview
又使用了 insertSublayer
的话你从 subviews
里面拿到的 index
完全是错误的。
最关键的一点是 exchangeSubview
方法中交换所使用的 index
并非是 view
层级的,而是 layer
层级的。这一点在官方文档里面压根没提,网上资料也搜不到关于这个问题的相关解答,让我着实踩了几个小时的坑。
正解
综上,exchangeSubview
方法正确的打开方式应该是这样子的:
let view1 = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 200))
view.addSubview(view1)
let view2 = UIView(frame: CGRect(x: 70, y: 120, width: 200, height: 200))
view.addSubview(view2)
let layer1 = CALayer()
view.layer.insertSublayer(layer1, at: 0)
/// 请忽略这里的强转,真实场景里要避免强转
let view1Index = view.layer.sublayers!.firstIndex(of: view1.layer)! // index为1
let view2Index = view.layer.sublayers!.firstIndex(of: view2.layer)! // index为2
view.exchangeSubview(at: view1Index, withSubviewAt: view2Index)
DONE!