How would I loop through all UIButtons in my view in Swift? I would want to set all the titles to "", but my for-loop in Swift is giving an error.
for btns in self.view as [UIButton] {
// set the title to ""
}
This code should work:
for view in self.view.subviews as [UIView] {
if let btn = view as? UIButton {
btn.setTitleForAllStates("")
}
}
You need to iterate through the subViews array.
Shortened and updated for Swift 3 & 4
for case let button as UIButton in self.view.subviews {
button.setTitleForAllStates("")
}
Looping over subview works, but it's sometimes a little ugly, and has other issues.
If you need to loop over some specific buttons, for example to add corner radius or change tint or background color, you can use an array of IBOutlets and then loop over that.
var buttons = [SkipBtn, AllowBtn]
for button in buttons as! [UIButton] {
button.layer.cornerRadius = 5
}
Swift 4:
let subviewButtons = self.view.subviews.filter({$0.isKind(of: UIButton.self)})
for button in subviewButtons {
//do something
}
To add some context for a common use case, suppose the buttons were in a scroll view and you wanted to highlight the tapped button and de-highlight the other buttons. In this situation, you would direct all buttons to one action method:
#objc private func buttonAction(_ button: UIButton) {
for case let b as UIButton in view.scrollView.subviews {
if b == button {
b.setTitleColor(UIColor.green, for: []) // highlight
} else {
b.setTitleColor(UIColor.black, for: []) // de-highlight
}
}
}
This code seems to be quite useful for iterating over any object within a view, just change UIButton for any other subview type such as UIView or UIImageView, etc.
let filteredSubviews = self.view.subviews.filter({
$0.isKindOfClass(UIButton)})
for view in filteredSubviews {
//Do something
}
Used some of the offered questions out there and created my own. I believe is the most efficient when you want to programmatically set up the title of various UIButtons(in my case I am building a quiz)
By randomising my array list and with just a for loop I printing the item at index to the button title
for view in self.viewForButtons.subviews{
if view.isKindOfClass(UIButton)
{
let button : UIButton = view as! UIButton
button.setTitle("item[i]", forState: .Normal)
}
}
If you have UIView's within self.view then you need to loop through the subviews while searching for UIButton. Using the accepted answer, I made this little function to do so:
Swift 4 + :
func findButton(`in` view: UIView){
for view in view.subviews as [UIView] {
if let button = view as? UIButton {
// Do something with 'button'
}else{
// Loop through subview looking for buttons
findButton(in: view)
}
}
}
Usage:
override func viewDidLoad() {
findButton(in: self.view)
}
Hope this helps!
Here's a short way in Swift if you know the subview only has buttons:
myView.subviews.map {
($0 as? UIButton)!.enabled = false
}
Related
I have 5 buttons within a UIStackView, and I want to find out which index is being selected, and later compare those indexes. My code right now gives me an Array.Index. I've tried both subviews and arrangedSubviews. Is there anyway I can turn this into an Integer? I can't figure it out. Thanks!!
if let selectedIndex = stackview.subviews.index(of: sender) {
}
// UPDATE
I kinda got what I wanted with:
let int = stackview.subviews.distance(from: stackview.subviews.startIndex, to: selectedIndex)
I'm still not sure if this is the most efficient way, but it does the job for now.
index(of:) return Int.
Also you should find your button in the arrangedSubviews, not in the subviews
Assuming your stack view contains only buttons, and each button is connected to this #IBAction, this should work:
#IBAction func didTap(_ sender: Any) {
// make sure the sender is a button
guard let btn = sender as? UIButton else { return }
// make sure the button's superview is a stack view
guard let stack = btn.superview as? UIStackView else { return }
// get the array of arranged subviews
let theArray = stack.arrangedSubviews
get the "index" of the tapped button
if let idx = theArray.index(of: btn) {
print(idx)
} else {
print("Should never fail...")
}
}
I would add a tag to each of your buttons (button.tag = index) then check the tag of your sender.
So then you can wire up each of your buttons to the same function with a sender parameter, then check if sender.tag == index.
In Swift 3 (XCode 8.3.3) I have a control in a UIStackView. I have an array of UIImageViews, and loop through the array to populate the stack view at run time:
for voiceIcon in voiceIcons {
let voiceView = UIImageView(image: voiceIcon)
addArrangedSubview(voiceView)
}
These icons will sometimes become disabled (replaced with a new image), so in order to update the control, I have a function to remove all the icons so that I can re-add the appropriate ones (if there's a better way, I'm listening!):
private func resetIconsView() {
for subUIView in self.subviews as [UIView] {
removeArrangedSubview(subUIView)
subUIView.removeFromSuperview()
print("Removing")
}
}
I've also tried
for subUIView in self.subviews as! [UIImageView] { ... }
I get the debug line "Removing" for each of the icons, but they still remain in the control and the UI. I'm new to Swift, so I'm likely not understanding something, what approach should I take?
Try code below:
for view in arrangedSubviews {
view.removeFromSuperview()
}
I am assuming your UIStackView only contains some UIImageView. You can iterate through all the arranged subviews of your stack view and update your image of that imageView. A sample implementation could look like below:
func changeImage() {
for view in self.arrangedSubviews {
if let imgView = view as? UIImageView {
imgView.image = UIImage(named: "taka_icon.png")
}
}
}
I did it with an extension. You have also to remove Constraints if existing. Else this can cause some trouble.
USAGE: myStackView.removeAllArrangedSubviews()
public extension UIStackView {
func removeAllArrangedSubviews() {
let removedSubviews = arrangedSubviews.reduce([]) { (allSubviews, subview) -> [UIView] in
self.removeArrangedSubview(subview)
return allSubviews + [subview]
}
// Deactivate all constraints
NSLayoutConstraint.deactivate(removedSubviews.flatMap({ $0.constraints }))
// Remove the views from self
removedSubviews.forEach({ $0.removeFromSuperview() })
}
}
I want to loop through view and its subviews to highlight them. Firstly, I use selector like this:
func highlightViewAndSubviews(view: UIView, highlighted: Bool) {
if view.respondsToSelector(Selector("setHighlighted:")) {
view.performSelector(Selector("setHighlighted:"), withObject: highlighted)
}
for subview in view.subviews {
highlightViewAndSubviews(subview, highlighted: highlighted)
}
}
But it doesn't work when highlighted is false or view is UILabel. Then I use type cast to achieve this. It works perfectly.
func highlightViewAndSubviews(view: UIView, highlighted: Bool) {
if let imageView = view as? UIImageView {
imageView.highlighted = highlighted
}
if let label = view as? UILabel {
label.highlighted = highlighted
}
for subview in view.subviews {
highlightViewAndSubviews(subview, highlighted: highlighted)
}
}
My question is why. And I prefer selector because it works for label,button,image view and so on. Many thanks in advance.
The problem is that this line doesn't do what you think:
view.performSelector(Selector("setHighlighted:"), withObject: highlighted)
Use key-value coding instead:
view.setValue(highlighted, forKey:"highlighted")
I have some buttons on the view. I don't know how many, I know that it can be minimum of 2 or maximum of 4. I want to check each button's title text and add to array true or false depending on value of title.
is it possible to do ?
Try below thing..
for view in self.view.subviews {
if view.isKindOfClass(UIButton){
// Add you logic over here.
// you can check the tag of button as well.
}
}
Yes, it is possible to iterate through all objects inside the view. To see all the subviews present in your view, you can do following:
for view in self.view.subviews as! [UIView] {
if let btn = view as? UIButton {
if btn.title == "whateverYourCriteriaIs" {
//your code
}
else {
//do something else
}
}
}
I created multiple buttons with unique tags and added the buttons to the UIView of my project. I want to re-access them so I can add the buttons into a views dictionary for autolayout.
func createButtons() {
for index in 0...10 {
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.tag = index
self.view.addSubview(button)
}
Code to create a views dictionary for autolayout purposes.
func createDictionary() {
var mbd : [String:UIButton]! = [:]
for index in 0...10 {
let tmpButton = self.view.viewWithTag(index) as? UIButton
mbd["button" + String(index)] = tmpButton
}
return mbd
}
However, I don't think the above code is creating any dictionary and I am not sure why. If I shift the code which I append to the dictionary in the first for loop then everything works. But I am trying to learn to use the viewWithTag method as I will need it frequently.
Any guidance is appreciated.
If you dont need the tags for anything else, except for creating the dictionary, you could create the dictionary while you create the buttons.
I would suggest something like this:
func createButtons (){
var mbd : [String:UIButton]! = [:]
for index in 0...10 {
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
self.view.addSubview(button)
mbd["button" + String(index)] = button
}
}
This solution is also more performant, in the sense that you don't call the loop twice and, since you already have the buttons, you don't need to call viewWithTag anymore.
Hope this works for you. Let me know how it goes!