I have a custom class of buttons in a UIView that I'd like to add to an array so that they're easily accessible. Is there a way to get all subviews of a specific class and add it to an array in Swift?
问题:
回答1:
The filter
function using the is
operator can filter items of a specific class.
let myViews = view.subviews.filter{$0 is MyButtonClass}
MyButtonClass
is the custom class to be filtered for.
回答2:
Here you go
extension UIView {
/** This is the function to get subViews of a view of a particular type
*/
func subViews<T : UIView>(type : T.Type) -> [T]{
var all = [T]()
for view in self.subviews {
if let aView = view as? T{
all.append(aView)
}
}
return all
}
/** This is a function to get subViews of a particular type from view recursively. It would look recursively in all subviews and return back the subviews of the type T */
func allSubViewsOf<T : UIView>(type : T.Type) -> [T]{
var all = [T]()
func getSubview(view: UIView) {
if let aView = view as? T{
all.append(aView)
}
guard view.subviews.count>0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
You can call it like
let allSubviews = view.allSubViewsOf(type: UIView.self)
let allLabels = view.allSubViewsOf(type: UILabel.self)
回答3:
To do this recursively (I.e. fetching all subview's views aswell), you can use this generic function:
private func getSubviewsOf<T : UIView>(view:UIView) -> [T] {
var subviews = [T]()
for subview in view.subviews {
subviews += getSubviewsOf(view: subview) as [T]
if let subview = subview as? T {
subviews.append(subview)
}
}
return subviews
}
To fetch all UILabel's in a view hierarchy, just do this:
let allLabels : [UILabel] = getSubviewsOf(view: theView)
回答4:
I can't test it right now but this should work in Swift 2:
view.subviews.flatMap{ $0 as? YourView }
Which returns an array of YourView
Here's a tested, typical example, to get a count:
countDots = allDots!.view.subviews.flatMap{$0 as? Dot}.count
回答5:
If you want to update/access those specific subviews then use this,
for (index,button) in (view.subviews.filter{$0 is UIButton}).enumerated(){
button.isHidden = false
}
回答6:
From Swift 4.1, you can use new compactMap (flatMap is now depcrecated): https://developer.apple.com/documentation/swift/sequence/2950916-compactmap (see examples inside)
In your case, you can use:
let buttons:[UIButton] = stackView.subviews.compactMap{ $0 as? UIButton }
And you can execute actions to all buttons using map:
let _ = stackView.subviews.compactMap{ $0 as? UIButton }.map { $0.isSelected = false }
回答7:
So many of the answers here are unnecessarily verbose or insufficiently general. Here's how to get all subviews of a view, at any depth, that are of any desired class:
extension UIView {
func subviews<T:UIView>(ofType WhatType:T.Type) -> [T] {
var result = self.subviews.compactMap {$0 as? T}
for sub in self.subviews {
result.append(contentsOf: sub.subviews(ofType:WhatType))
}
return result
}
}
How to use:
let arr = myView.subviews(ofType: MyButtonClass.self)
回答8:
func allSubViews(views: [UIView]) {
for view in views {
if let tf = view as? UITextField {
// Do Something
}
self.allSubViews(views: view.subviews)
}
}
self.allSubViews(views: self.view.subviews)
回答9:
For this case, I think we could use Swift's first.where
syntax, which is more efficient than filter.count
, filter.isEmpty
.
Because when we use filter
, it will create a underlying array, thus not effective, imagine we have a large collection.
So just check if a view's subViews
collection contains a specific kind of class, we can use this
let containsBannerViewKind = view.subviews.first(where: { $0 is BannerView }) != nil
which equivalent to: find me the first match to BannerView class in this view's subViews collection. So if this is true, we can carry out our further logic.
Reference: https://github.com/realm/SwiftLint/blob/master/Rules.md#first-where
回答10:
Let me post my variation of this) but this, finds the first of T
extension UIView {
func firstSubView<T: UIView>(ofType type: T.Type) -> T? {
var resultView: T?
for view in subviews {
if let view = view as? T {
resultView = view
break
}
else {
if let foundView = view.firstSubView(ofType: T.self) {
resultView = foundView
break
}
}
}
return resultView
}
}