Jonathan Tuzman
Jonathan Tuzman

Reputation: 13298

Swift: Finding views in a superview (without a for loop)

I'm using the following code to find a certain subview inside my view. It works fine, and the for loop runs only once because it only finds one subview that satisfies the where clause.

for view in boardView.subviews where (view as? PlayingCardView)?.position.location == source.
    && (view as! PlayingCardView).position.column == source.column
    && (view as! PlayingCardView).position.row >= source.row

However, it seems weird that I'm running a loop here when I'm not actually doing any...loop-y things (know what I mean)?

Is there some other way to "search" like this? Like a way to use subviews.index(of:) and use the conditions from the where clause, or something like that?


Also, I know that I could do the same code like this:

for view in boardView.subviews {
   if let cardView = view as? PlayingCardView {
      if cardView.position.location == source.location
        && (view as! PlayingCardView).position.column == source.column
        && (view as! PlayingCardView).position.row >= source.row {
            // Do stuff
        }
    }
}

Is one of these ways computationally faster?

Upvotes: 0

Views: 263

Answers (2)

Callam
Callam

Reputation: 11539

I believe you're looking for the filter method.

if let card = (boardView.subviews as? [PlayingCardView])?.filter({
    return $0.position.location == source.location
        && $0.position.column == source.column
        && $0.position.row >= source.row
}).first {
    // Do stuff
    print(card)
}

Or if you want to find the first card that satisfies your arguments, you can use the first method.

if let card = (boardView.subviews as? [PlayingCardView])?.first(where: {
    return $0.position.location == source.location
        && $0.position.column == source.column
        && $0.position.row >= source.row
}) {
    // Do stuff
    print(card)
}

Upvotes: 1

Code Different
Code Different

Reputation: 93191

first(where: ) will give you the first element in the array that satisfies a condition (assuming that you only want to do it on one element since it's not "loopy"):

let view = boardView.subviews.first {
    guard let cardView = $0 as? PlayingCardView else { return false }
    return cardView.position.location == source.location
            && cardView.position.column == source.column
            && cardView.position.row >= source.row
}

// do stuffs to `view`

It will stop as soon as a match is found so that's about as efficient as you can get. In reality though, it hardly matters as you tend to have only a small number of subviews. Otherwise the GPU will give out first from having to render all of them.

Upvotes: 0

Related Questions