Skip to content

Instantly share code, notes, and snippets.

@DevAndArtist
Last active December 24, 2017 12:16
Show Gist options
  • Select an option

  • Save DevAndArtist/7dbe7f505265dc292aa6076a54ced459 to your computer and use it in GitHub Desktop.

Select an option

Save DevAndArtist/7dbe7f505265dc292aa6076a54ced459 to your computer and use it in GitHub Desktop.
import UIKit

protocol ViewComponent : AnyObject {
  ///
  func isAncestor(of descendant: Self) -> Bool

  ///
  func isDescendant(of ancestor: Self) -> Bool

  ///
  func detach()
}

extension ViewComponent where Self : UIView {
  ///
  func attach<Ancestor>(
    to ancestor: Ancestor,
    performing attachment: (
      (ancestor: Ancestor, current: Self)
    ) -> Void
  ) where Ancestor : UIView {
    attachment((ancestor, self))
    assert(
      ancestor.isAncestor(of: self),
      """
      After attachment closure was called, `ancestor` \
      must be an ancestor of `self`.
      """
    )
  }

  ///
  /* default */ func detach() {
    removeFromSuperview()
  }
}

extension ViewComponent where Self : UIViewController {
  ///
  func attach<Ancestor>(
    to ancestor: Ancestor,
    performing attachment: (
      (ancestor: Ancestor, current: Self)
    ) -> Void
  ) where Ancestor : UIViewController {
    ancestor.addChildViewController(self)
    attachment((ancestor, self))
    assert(
      ancestor.isAncestor(of: self) &&
      ancestor.view.isAncestor(of: self.view),
      """
      After attachment closure was called, `ancestor` \
      must be an ancestor of `self` and also `ancestor.view` \
      must be an ancestor of `self.view`.
      """
    )
    didMove(toParentViewController: ancestor)
  }

  ///
  /* default */ func detach() {
    willMove(toParentViewController: nil)
    view.detach()
    removeFromParentViewController()
  }
}

extension UIView : ViewComponent {
  ///
  func isAncestor(of descendant: UIView) -> Bool {
    var ancestor = descendant.superview
    while let someAncestor = ancestor {
      if self === someAncestor {
        return true
      }
      ancestor = someAncestor.superview
    }
    return false
  }

  ///
  func isDescendant(of ancestor: UIView) -> Bool {
    return ancestor.isAncestor(of: self)
  }
}

extension UIViewController : ViewComponent {
  ///
  func isAncestor(of descendant: UIViewController) -> Bool {
    var ancestor = descendant.parent
    while let someAncestor = ancestor {
      if self === someAncestor {
        return true
      }
      ancestor = someAncestor.parent
    }
    return false
  }

  ///
  func isDescendant(of ancestor: UIViewController) -> Bool {
    return ancestor.isAncestor(of: self)
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment