3

I've written a function that iterates through the superviews of a given UIView to obtain a reference to a specific UIView subclass if present (in this instance, UITableView). This works fine using imperative style but it does seem to be a problem that would lend itself very well to 'Railway Oriented Programming'. As I'm still getting my head around functional, is anyone able to suggest a more elegant functional version of this function?

func findTableView(var view: UIView) -> UITableView? {
    var table: UITableView? = nil
    while table == nil {
        guard let superView = view.superview else { return nil }
        view = superView
        table = view as? UITableView
    }
    return table
}
rustproofFish
  • 931
  • 10
  • 32

3 Answers3

8

Like this?

func findTableView(view: UIView) -> UITableView? {
    return view as? UITableView ?? view.superview.flatMap(findTableView)
}

This code is just a short-cut of:

func findTableView(view: UIView) -> UITableView? {
    if let tableView = view as? UITableView {
        return tableView
    }
    else {
        let superview = view.superview
        if superview == nil {
            return nil
        }
        else {
            return findTableView(superview!)
        }
    }
}

Using "Nil Coalescing Operator" and flatMap(_:) method on Optional enum.

rintaro
  • 51,423
  • 14
  • 131
  • 139
  • That's it! I knew if would be more concise. I don't suppose you could you elaborate on this? I'm having a slight conceptual issue understanding what the code is doing. – rustproofFish Oct 06 '15 at 14:02
  • Thanks! Obvious when you see the expanded version. The use of flatmap() was confusing me but now I realise it's a rather cunning way of handling the nil event when you run out of superviews. – rustproofFish Oct 07 '15 at 15:19
4

Swift find superview of given class with generics

Swift 3/4

extension UIView {

    func superview<T>(of type: T.Type) -> T? {
        return superview as? T ?? superview.compactMap { $0.superview(of: type) }
    }

    func subview<T>(of type: T.Type) -> T? {
        return subviews.compactMap { $0 as? T ?? $0.subview(of: type) }.first
    }

}

Usage:

let tableView = someView.superview(of: UITableView.self)
let tableView = someView.subview(of: UITableView.self)
efremidze
  • 2,640
  • 1
  • 25
  • 35
3

Edit: This is for searching through the subviews not superviews

You could try a recursive functional approach without a while. Don't know if that is what you are looking for.

func findTableView(view: UIView) -> UITableView? {
    if view is UITableView {
        return view as? UITableView
    } else {
        for subview in view.subviews {
            if let res = findTableView(subview) {
                return res
            }
        }
    }
    return nil
}

Edit 2 + 3: Made the function simpler

kutschenator
  • 902
  • 10
  • 26
  • I actually did think of that as the problem would seem to lend itself well to it but I found it actually made my code slightly bulkier and harder to read. I was thinking that i could use a recursive 'pipeline' if that makes sense: get superview -> if no view, exit with nil otherwise) -> if superview return superview otherwise repeat. I'm not sure if I've made myself clear as I'm not up on the terminology. Thanks for the suggestion though – rustproofFish Oct 06 '15 at 12:08
  • oh i just realized you want to search "up" and not "down" – kutschenator Oct 06 '15 at 12:10
  • No problem :-) Thanks anyway – rustproofFish Oct 06 '15 at 13:58