2

In my ViewController, I have 12 custom UIViews(named CardView).

enter image description here

I am trying to iterate through the subviews of the ViewController programmatically to find the custom view(CardView) and do some configuration. Following is my code to count the number of CardViews.

    private func cardCount()->Int{
            var count = 0
            for subview in self.view.subviews{
                if subview is CardView{
                    count = count + 1
                }
            }
            return count
    }

However, it is returning me '0' and the reason being my views are embedded inside UIStackViews. I have 3 horizontally aligned stack views inside a vertically aligned one like-

enter image description here

How can I get my CardViews programmatically. Any help would be appreciated.

Manoj Rlogical
  • 239
  • 1
  • 4
Natasha
  • 6,651
  • 3
  • 36
  • 58
  • 1
    use the property arrangedSubviews instead of subviews – Diptesh May 03 '19 at 05:45
  • 1
    You can use collectionView for this kind of UI.That one is better approach – PinkeshGjr May 03 '19 at 05:45
  • Dear I think you can loop again and again to access cardviews – Ajjjjjjjj May 03 '19 at 05:45
  • first loop for vertical stackview, second for horizontal stackviews and last for accessing card – Ajjjjjjjj May 03 '19 at 05:46
  • I think, you should change the ui rendering logic here by using either stack view inside collection view item or table view cell. But for answering your query here, you can create an instance of the container `stack view` and iterate for the sub `stack views`. That way you will get individual `stack views` and again second iteration would be for the individual `stack views` for getting `subviews` of it. – Vijay Sanghavi May 03 '19 at 05:52
  • Can you explain why you are counting the number of cards? I smell an XY question. – Sweeper May 03 '19 at 06:00
  • Thank you for all the suggestions. I know there are quite a few better alternatives in this situation but I am doing it for learning purpose. I think, I need to loop in until there is no more stackviews. – Natasha May 03 '19 at 06:41

5 Answers5

2

You can do a few flat maps to flatten your view structure first, then count them:

private func cardCount()->Int{

    var count = 0
    for subview in self.view.subviews.flatMap { $0.subviews }.flatMap { $0.subviews }{
        if subview is CardView{
            count = count + 1
        }
    }
    return count

}

But I feel like you are doing things the wrong way around. The number of cards sounds like something in your model. You should have a variable called cardCount and the cards on the screen are supposed to change according to that variable, not the other way around.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
1

You create IBOutlets to each of the horizontal stack views. Then loop through the subviews in the stackviews.

@IBOutlet weak var StackView1: UIStackView!
@IBOutlet weak var StackView2: UIStackView!
@IBOutlet weak var StackView3: UIStackView!



  for customView in StackView1.arrangedSubviews
  {
     // Do task
  }
Merl
  • 305
  • 2
  • 9
0

Try this

private func cardCount() -> Int
{
    var count = 0
    for subview in self.view.subviews
    {
        if let stackView: UIStackView = subview as? UIStackView
        {
            let arrangedViews = stackView.arrangedSubviews
            for cardView in arrangedViews
            {
                if cardView is CardView
                {
                    count = count + 1
                }
            }

        }
    }
    return count
}
Diptesh
  • 383
  • 1
  • 5
  • 14
0

From the pictorial diagram you shared, it seems the StackView is the first child. Whenever you get the subviews, only the first child views are returned. So self.view.subviews would result in only one UIStackView. Give a tag to each stack view. Then, in code :

private func cardCount()->Int{
        var count = 0
        for subview in self.view.subviews{
            if subview.tag == 10 {  // parent stack view
                 for subviewStack in subview {  // Get subviews of parent stackview
                      if subviewStack.tag == 11 {  // First stack view child
                          for subViewSubStack in subviewStack.subviews { // Get card views in this stack view
                              // Apply your count logic 
                          }
                      }
                 }

            }
        }
        return count
}  

I have written only the first condition. You might add others.
Having said this, I won't say this is the most optimum solution. This can and should be refactored.

Nitish
  • 13,845
  • 28
  • 135
  • 263
0

Create an extension method like this. It will count all CardView instances in the view controller.

extension UIView {
    func cardCount() -> Int {
        switch self {
        case let self as CardView:
            return 1
        case let self as UIStackView:
            return self.arrangedSubviews.reduce(0, { $0 + $1.cardCount() })
        default:
            return self.subviews.reduce(0, { $0 + $1.cardCount() })
        }
    }
}

and call this method in viewDidLoad

class ViewControllerd: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print(self.view.cardCount())
    }
}
RajeshKumar R
  • 15,445
  • 2
  • 38
  • 70