relative-view 1.0.2

relative-view 1.0.2

Jae Yeum 维护。



  • Jae Yeum

Build Status Coverage Status

relative-view

relative-view 是一个为 UIView 提供专门方法/操作的 UIKit 扩展,用于查找相对的 UIViews。这个框架的目标是提供一个框架,其中

  • 用户可以按他们希望的方式查找和组合其他 UIViews
  • 该框架已完全单元测试(100% 代码覆盖率)。

在此框架中,“相对”一词表示一个 UIView 不能是其自身的祖先、后代或同级。相对性也是无限延伸的,这意味着此框架考虑一个 UIView foo 是另一个 UIView bar 的祖先、后代或同级,即使它们之间有任意数量的 UIViews。因此,相对性不是指 foobar 直接相邻。

以下是一个方法/操作列表以及一些示例。


  • findFirstAncestor(by: (UIView) -> Bool) -> UIView? 通过这个重载传递一个闭包 (UIView) -> Bool。此闭包会在每个祖先上调用。如果祖先满足您的条件,则返回最近的最先匹配的祖先。
  • findFirstAncestor(by: [UIView.Type]) -> UIView? 通过这个重载,祖先通过一个 UIView.TypeArray 进行评估,返回那个 Array 中的 UIView.Type 的最近祖先。
  • findFirstAncestor(by: [Int]) -> UIView? 通过这个重载,祖先通过一个代表 tagIntArray 进行评估,返回那个 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]] 使用这个重载,你提供了一个 ArrayUIView.Type,只有当 UIView.Type 存在于此 Array 中时,祖先才会被分组,而这些组是按照描述 UIView.TypeString 来命名的。
  • groupAncestors(by: [Int]) -> [Int : [UIView]] 使用这个重载,你提供了一个代表 tagsInt 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.TypeArray,只有当 UIView.Type 存在于此 Array 中时,后代才会被分组,而这些组是按照描述 UIView.TypeString 来命名的。
  • groupDescendants(by: [Int]) -> [Int : [UIView]] 使用这个重载,你提供了一个代表 tagsInt 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 不能是其自身的相对后代),并且 foobar 都有非空的 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.TypeArray,只有当 UIView.Type 存在于此 Array 中时,兄弟才会被分组,而这些组是按照描述 UIView.TypeString 来命名的。
  • groupSiblings(by: [Int]) -> [Int : [UIView]] 使用这个重载,你提供了一个代表 tagsInt 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])