Skip to content

Instantly share code, notes, and snippets.

@Revolucent
Last active October 28, 2018 07:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Revolucent/57243a43993a5ff5d5ee755542c14538 to your computer and use it in GitHub Desktop.
Save Revolucent/57243a43993a5ff5d5ee755542c14538 to your computer and use it in GitHub Desktop.
Functions for recursively finding ancestor and descendants in Swift. Useful for creating extension methods.
public func descendants<Descendant>(of parent: Descendant, in attribute: KeyPath<Descendant, [Descendant]>, where: (Descendant) throws -> Bool) rethrows -> [Descendant] {
var descendants: [Descendant] = []
for child in parent[keyPath: attribute] {
if try `where`(child) {
descendants.append(child)
}
try descendants.append(contentsOf: MASCore.descendants(of: child, in: attribute, where: `where`))
}
return descendants
}
public func descendants<Descendant>(of parent: Descendant, in attribute: KeyPath<Descendant, [Descendant]>, where: KeyPath<Descendant, Bool>) -> [Descendant] {
return descendants(of: parent, in: attribute, where: { $0[keyPath: `where`] })
}
public func descendants<Parent, Descendant>(of parent: Parent, in attribute: KeyPath<Parent, [Parent]>, ofType type: Descendant.Type = Descendant.self) -> [Descendant] {
var descendants: [Descendant] = []
for child in parent[keyPath: attribute] {
if let descendant = child as? Descendant {
descendants.append(descendant)
}
descendants.append(contentsOf: MASCore.descendants(of: child, in: attribute, ofType: type))
}
return descendants
}
public func firstDescendant<Descendant>(of parent: Descendant, in attribute: KeyPath<Descendant, [Descendant]>, where: (Descendant) throws -> Bool) rethrows -> Descendant? {
for child in parent[keyPath: attribute] {
if try `where`(child) { return child }
if let descendant = try firstDescendant(of: child, in: attribute, where: `where`) {
return descendant
}
}
return nil
}
public func firstDescendant<Descendant>(of parent: Descendant, in attribute: KeyPath<Descendant, [Descendant]>, where: KeyPath<Descendant, Bool>) -> Descendant? {
return firstDescendant(of: parent, in: attribute, where: { $0[keyPath: `where`] })
}
public func firstDescendant<Parent, Descendant>(of parent: Parent, in attribute: KeyPath<Parent, [Parent]>, ofType type: Descendant.Type = Descendant.self) -> Descendant? {
for child in parent[keyPath: attribute] {
if let descendant = child as? Descendant {
return descendant
}
if let descendant = firstDescendant(of: child, in: attribute, ofType: type) {
return descendant
}
}
return nil
}
public func ancestor<Ancestor>(of child: Ancestor, in attribute: KeyPath<Ancestor, Ancestor?>, where: (Ancestor) throws -> Bool) rethrows -> Ancestor? {
if let parent = child[keyPath: attribute] {
if try `where`(parent) { return parent }
return try ancestor(of: parent, in: attribute, where: `where`)
}
return nil
}
public func ancestor<Ancestor>(of child: Ancestor, in attribute: KeyPath<Ancestor, Ancestor?>, where: KeyPath<Ancestor, Bool>) -> Ancestor? {
return ancestor(of: child, in: attribute, where: { $0[keyPath: `where`] })
}
public func ancestor<Parent, Ancestor>(of child: Parent, in attribute: KeyPath<Parent, Parent?>, ofType type: Ancestor.Type = Ancestor.self) -> Ancestor? {
if let parent = child[keyPath: attribute] {
if let ancestor = parent as? Ancestor {
return ancestor
}
return ancestor(of: parent, in: attribute, ofType: type)
}
return nil
}
@Revolucent
Copy link
Author

Recursively find the first responder:

firstDescendant(of: view, in: \.subviews, where: { $0.isFirstResponder})

or

firstDescendant(of: view, in: \.subviews, where: \.isFirstResponder)

@Revolucent
Copy link
Author

Find a subview of a UINavigationController, such as in a segue:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let child = firstDescendant(of: segue.destination, in: \.children, ofType: ChildViewController.self) {
        // Do something here
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment