Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Recursively cycle through all subviews to find a specific class and append to an array

Having a devil of a time trying to figure this out. I asked a similar question here: Swift: Get all subviews of a specific type and add to an array

While this works, I realized there are many subviews and sub-sub views, and so I need a function that starts at the main UIView, cycles through all the subviews (and their subviews until there aren't any left) and adds it to an array for a custom button class which I have named CheckCircle.

Essentially I'd like to end up with an array of CheckCircles which constitute all the CheckCircles added to that view programmatically.

Any ideas? Here's what I've been working on. It doesn't seem to be appending any Checkcircles to the array:

    func getSubviewsOfView(v:UIView) -> [CheckCircle] {         var circleArray = [CheckCircle]()         // Get the subviews of the view          var subviews = v.subviews          if subviews.count == 0 {             return circleArray         }          for subview : AnyObject in subviews{   if let viewToAppend = subview as? CheckCircle {         circleArray.append(viewToAppend as CheckCircle)       }             getSubviewsOfView(subview as! UIView)         }         return circleArray     } 
like image 951
Kenji Crosland Avatar asked Aug 30 '15 21:08

Kenji Crosland


1 Answers

Based on Aaron Brager and ullstrm answers

Details

  • Xcode 9.1, Swift 4,
  • Xcode Version 10.3 (10G8), Swift 5

Solution

extension UIView {      class func getAllSubviews<T: UIView>(from parenView: UIView) -> [T] {         return parenView.subviews.flatMap { subView -> [T] in             var result = getAllSubviews(from: subView) as [T]             if let view = subView as? T { result.append(view) }             return result         }     }      class func getAllSubviews(from parenView: UIView, types: [UIView.Type]) -> [UIView] {         return parenView.subviews.flatMap { subView -> [UIView] in             var result = getAllSubviews(from: subView) as [UIView]             for type in types {                 if subView.classForCoder == type {                     result.append(subView)                     return result                 }             }             return result         }     }      func getAllSubviews<T: UIView>() -> [T] { return UIView.getAllSubviews(from: self) as [T] }     func get<T: UIView>(all type: T.Type) -> [T] { return UIView.getAllSubviews(from: self) as [T] }     func get(all types: [UIView.Type]) -> [UIView] { return UIView.getAllSubviews(from: self, types: types) } } 

Usage sample

var allViews = UIView.getAllSubviews(from: simpleView) func printResult(with text: String) {     print("\n==============================================")     print("\(text):\n\(allViews.map { $0.classForCoder } )") } printResult(with: "UIView.getAllSubviews(from: simpleView)")  allViews = UIView.getAllSubviews(from: simpleView) as [UILabel] printResult(with: "UIView.getAllSubviews(from: simpleView) as [UILabel]")  allViews = UIView.getAllSubviews(from: simpleView, types: [UIStackView.self, UILabel.self]) printResult(with: "UIView.getAllSubviews(from: simpleView, types: [UIStackView.self, UILabel.self])")  allViews = simpleView.getAllSubviews() printResult(with: "simpleView.getAllSubviews()")  allViews = simpleView.getAllSubviews() as [UILabel] printResult(with: "simpleView.getAllSubviews() as [UILabel]")  allViews = simpleView.get(all: UILabel.self) printResult(with: "simpleView.get(all: UILabel.self)")  allViews = simpleView.get(all: [UIStackView.self, UILabel.self]) printResult(with: "simpleView.get(all: [UIStackView.self, UILabel.self])") 

Output of the sample

============================================== UIView.getAllSubviews(from: simpleView): [UILabel, UIButton, UILabel, UILabel, UILabel, UIStackView]  ============================================== UIView.getAllSubviews(from: simpleView) as [UILabel]: [UILabel, UILabel, UILabel, UILabel]  ============================================== UIView.getAllSubviews(from: simpleView, types: [UIStackView.self, UILabel.self]): [UILabel, UILabel, UILabel, UILabel, UIStackView]  ============================================== simpleView.getAllSubviews(): [UILabel, UIButton, UILabel, UILabel, UILabel, UIStackView]  ============================================== simpleView.getAllSubviews() as [UILabel]: [UILabel, UILabel, UILabel, UILabel]  ============================================== simpleView.get(all: UILabel.self): [UILabel, UILabel, UILabel, UILabel]  ============================================== simpleView.get(all: [UIStackView.self, UILabel.self]): [UILabel, UILabel, UILabel, UILabel, UIStackView] 

Storyboard of the sample

enter image description here

Other info

Also, I suggest to work with weak references. Array with weak references to objects

like image 66
Vasily Bodnarchuk Avatar answered Sep 21 '22 12:09

Vasily Bodnarchuk