relative-view
relative-view
是一个为 UIView
提供专门方法/操作的 UIKit
扩展,用于查找相对的 UIViews
。这个框架的目标是提供一个框架,其中
- 用户可以按他们希望的方式查找和组合其他
UIViews
。 - 该框架已完全单元测试(100% 代码覆盖率)。
在此框架中,“相对”一词表示一个 UIView
不能是其自身的祖先、后代或同级。相对性也是无限延伸的,这意味着此框架考虑一个 UIView
foo
是另一个 UIView
bar
的祖先、后代或同级,即使它们之间有任意数量的 UIViews
。因此,相对性不是指 foo
和 bar
直接相邻。
以下是一个方法/操作列表以及一些示例。
findFirstAncestor(by: (UIView) -> Bool) -> UIView?
通过这个重载传递一个闭包(UIView) -> Bool
。此闭包会在每个祖先上调用。如果祖先满足您的条件,则返回最近的最先匹配的祖先。findFirstAncestor(by: [UIView.Type]) -> UIView?
通过这个重载,祖先通过一个UIView.Type
的Array
进行评估,返回那个Array
中的UIView.Type
的最近祖先。findFirstAncestor(by: [Int]) -> UIView?
通过这个重载,祖先通过一个代表tag
的Int
的Array
进行评估,返回那个Array
中的tag
的最近祖先。
注意,如果在这些重载中的任何一个中超过一个祖先满足条件,则“第一个”(返回的)是定义为接近 self
(调用的 UIView
)的那个。
let view: UIView = UIView(frame: CGRect())
view.tag = 3
let ancestor: UITableViewCell = UITableViewCell(frame: CGRect())
ancestor.tag = 1
ancestor.addSubview(view)
// Find the first ancestor by closure (you define how it finds ancestors!).
XCTAssertTrue(ancestor === view.findFirstAncestor() {
(ancestor: UIView) -> Bool in
return ancestor.tag == 1
})
// Find the first ancestor by UIView.Type.
XCTAssertTrue(ancestor === view.findFirstAncestor(by: [UITableViewCell.self]))
// Find the first ancestor by tag.
XCTAssertTrue(ancestor === view.findFirstAncestor(by: [1]))
isRelativeAncestor(of: UIView) -> Bool
检查调用UIView
是否是另一个UIView
的相对祖先。
注意,一个 UIView
foo
只有在 foo !== bar
的情况下才只能是 UIView
bar
的相对祖先(这就是“相对”一词的含义,一个 UIView
不能是它自身的相对祖先)。
let view: UIView = UIView(frame: CGRect())
let ancestor: UITableViewCell = UITableViewCell(frame: CGRect())
ancestor.addSubview(view)
// Checks whether ancestor is a relative ancestor of view.
XCTAssertTrue(ancestor.isRelativeAncestor(of: view))
groupAncestors<Type: Hashable>(by: (UIView) -> Type?) -> [Type : [UIView]]
通过这个重载,您通过泛型定义要按其分组键的类型。您还传递一个闭包(UIView) -> Type?
,此闭包会在所有祖先上调用。如果祖先要按该键分组,则必须返回非空Type
键。返回 nil 键表示祖先不应分组。groupAncestors(by: [UIView.Type]) -> [String : [UIView]]
使用这个重载,你提供了一个Array
的UIView.Type
,只有当UIView.Type
存在于此Array
中时,祖先才会被分组,而这些组是按照描述UIView.Type
的String
来命名的。groupAncestors(by: [Int]) -> [Int : [UIView]]
使用这个重载,你提供了一个代表tags
的Int
Array
,只有当tag
存在于此Array
中时,祖先才会被分组,而这些组是按照tag
来命名的。
所有这些操作都是一样的,它们返回一个表示相对于 UIView
找到的祖先组的 Dictionary
。它们之间的不同之处在于查找和分组祖先的方式(通过哪种类型的关键)。事实上,后两种只是对第一种的便捷方法。
let view: UIView = UIView(frame: CGRect())
view.tag = 3
let ancestor: UITableViewCell = UITableViewCell(frame: CGRect())
ancestor.tag = 1
ancestor.addSubview(view)
// Group ancestors by closure (you define how it groups ancestors!).
let ancestorsByClosure: [Int : [UIView]] = view.groupAncestors() {
(ancestor: UIView) -> Int? in
if ancestor.tag == 1 {
return ancestor.tag
}
return nil
}
XCTAssertEqual(1, ancestorsByClosure.count)
XCTAssertEqual(1, ancestorsByClosure[1]!.count)
XCTAssertTrue(ancestor === ancestorsByClosure[1]![0])
// Group ancestors by UIView.Type.
let tableViewCellKey: String = String(describing: UITableViewCell.self)
let ancestorsByViewType: [String : [UIView]] = view.groupAncestors(by: [UITableViewCell.self])
XCTAssertEqual(1, ancestorsByViewType.count)
XCTAssertEqual(1, ancestorsByViewType[tableViewCellKey]!.count)
XCTAssertTrue(ancestor === ancestorsByViewType[tableViewCellKey]![0])
// Group ancestors by tag.
let ancestorsByTag: [Int : [UIView]] = view.groupAncestors(by: [1])
XCTAssertEqual(1, ancestorsByTag.count)
XCTAssertEqual(1, ancestorsByTag[1]!.count)
XCTAssertTrue(ancestor === ancestorsByTag[1]![0])
isRelativeDescendant(of: UIView) -> Bool
检查调用的UIView
是否是另一个UIView
的相对后代。
请注意,只有当 UIView
`foo` 与 UIView
`bar` 不相等(这就是“相对”的含义,一个 UIView
不能是其自身的相对后代)时,`foo` 才能是 `bar` 的相对后代。
后代性是通过深度优先遍历确定的。
let view: UIView = UIView(frame: CGRect())
let descendant: UITableViewCell = UITableViewCell(frame: CGRect())
view.addSubview(descendant)
// Checks whether descendant is a relative descendant of view.
XCTAssertTrue(descendant.isRelativeDescendant(of: view))
groupDescendants<Type: Hashable>(by: (UIView) -> Type?) -> [Type : [UIView]]
使用这个重载,你通过泛型定义了分组键的类型。你还传递了一个闭包(UIView) -> Type?
,这个闭包将在所有后代上调用,并且如果后代需要按该键分组,则必须返回一个非空的Type
键。返回 nil 键表示后代不应分组。groupDescendants(by: [UIView.Type]) -> [String : [UIView]]
使用这个重载,你提供了一个UIView.Type
的Array
,只有当UIView.Type
存在于此Array
中时,后代才会被分组,而这些组是按照描述UIView.Type
的String
来命名的。groupDescendants(by: [Int]) -> [Int : [UIView]]
使用这个重载,你提供了一个代表tags
的Int
Array
,只有当tag
存在于此Array
中时,后代才会被分组,而这些组是按照tag
来命名的。
所有这些操作都是一样的,它们返回一个表示相对于 UIView
找到的后代组的 Dictionary
。它们之间的不同之处在于查找和分组后代的方式(通过哪种类型的关键)。事实上,后两种只是对第一种的便捷方法。
后代性是通过深度优先遍历确定的。
let tableViewCell: UITableViewCell = UITableViewCell(frame: CGRect())
tableViewCell.tag = 3
let descendant: UIView = UIView(frame: CGRect())
descendant.tag = 1
tableViewCell.addSubview(descendant)
// Group descendants by closure (you define how it groups descendants!).
let descendantsByClosure: [Int : [UIView]] = tableViewCell.groupDescendants() {
(descendant: UIView) -> Int? in
if descendant.tag == 1 {
return descendant.tag
}
return nil
}
XCTAssertEqual(1, descendantsByClosure.count)
XCTAssertEqual(1, descendantsByClosure[1]!.count)
XCTAssertTrue(descendant === descendantsByClosure[1]![0])
// Group descendants by UIView.Type.
let viewKey: String = String(describing: UIView.self)
let descendantsByViewType: [String : [UIView]] = tableViewCell.groupDescendants(by: [UIView.self])
XCTAssertEqual(1, descendantsByViewType.count)
XCTAssertEqual(1, descendantsByViewType[viewKey]!.count)
XCTAssertTrue(descendant === descendantsByViewType[viewKey]![0])
// Group descendants by tag.
let descendantsByTag: [Int : [UIView]] = tableViewCell.groupDescendants(by: [1])
XCTAssertEqual(1, descendantsByTag.count)
XCTAssertEqual(1, descendantsByTag[1]!.count)
XCTAssertTrue(descendant === descendantsByTag[1]![0])
isRelativeSibling(of: UIView) -> Bool
检查调用UIView
是否是另一个UIView
的相对兄弟。
请注意,只有当 UIView
`foo` 与 UIView
`bar` 不相等(这就是“相对”的含义,一个 UIView
不能是其自身的相对后代),并且 foo
和 bar
都有非空的 superview
,而且两个的非空 superview
必须相同时,`foo` 才能是 `bar` 的相对兄弟。
let view: UIView = UIView()
let foo: UIButton = UIButton()
view.addSubview(foo)
let bar: UILabel = UILabel()
view.addSubview(bar)
// Checks whether foo is a relative sibling of bar (and vice-versa).
XCTAssertTrue(foo.isRelativeSibling(of: bar))
XCTAssertTrue(bar.isRelativeSibling(of: foo))
groupSiblings<Type: Hashable>(by: (UIView) -> Type?) -> [Type : [UIView]]
使用这个重载,你通过泛型定义了分组键的类型。你还传递了一个闭包(UIView) -> Type?
,这个闭包将在所有兄弟上调用,如果要将兄弟分组为该键,则需要返回一个非空的Type
键。返回 nil 键表示兄弟不应分组。groupSiblings(by: [UIView.Type]) -> [String : [UIView]]
使用这个重载,你提供了一个UIView.Type
的Array
,只有当UIView.Type
存在于此Array
中时,兄弟才会被分组,而这些组是按照描述UIView.Type
的String
来命名的。groupSiblings(by: [Int]) -> [Int : [UIView]]
使用这个重载,你提供了一个代表tags
的Int
Array
,只有当tag
存在于此Array
中时,兄弟才会被分组,而这些组是按照tag
来命名的。
所有这些操作方式相同,它们返回一个表示相对于一个UIView
查找到的兄弟分组Dictionary
。它们唯一的区别在于通过哪种类型的键来查找和组合兄弟元素。事实上,后两者仅仅是第一个函数的便捷方法。
let view: UIView = UIView()
let foo: UIButton = UIButton()
foo.tag = 1
view.addSubview(foo)
let bar: UILabel = UILabel()
bar.tag = 2
view.addSubview(bar)
// Group siblings by closure (you define how it groups siblings!).
let siblingsByClosure: [Int : [UIView]] = bar.groupSiblings() {
(sibling: UIView) -> Int? in
if sibling.tag == 1 {
return sibling.tag
}
return nil
}
XCTAssertEqual(1, siblingsByClosure.count)
XCTAssertEqual(1, siblingsByClosure[1]!.count)
XCTAssertTrue(foo === siblingsByClosure[1]![0])
// Group siblings by UIView.Type.
let buttonKey: String = String(describing: UIButton.self)
let siblingsByViewType: [String : [UIView]] = bar.groupSiblings(by: [UIButton.self])
XCTAssertEqual(1, siblingsByViewType.count)
XCTAssertEqual(1, siblingsByViewType[buttonKey]!.count)
XCTAssertTrue(foo === siblingsByViewType[buttonKey]![0])
// Group siblings by tag.
let siblingsByTag: [Int : [UIView]] = bar.groupSiblings(by: [1])
XCTAssertEqual(1, siblingsByTag.count)
XCTAssertEqual(1, siblingsByTag[1]!.count)
XCTAssertTrue(foo === siblingsByTag[1]![0])