Accessing to UIView contents of a UIScrollView in Swift - swift

I have a function that creates an scrollview "personsScrollView".
#IBOutlet var personsScrollView: UIScrollView!
var personsScroll = UIView()
personsScroll = createPersonsScroll(CGSizeMake(60.0, 80.0), avatarCount: 10)
func createPersonsScroll(buttonSize:CGSize, avatarCount:Int) -> UIView {
let padding = CGSizeMake(10, 10)
let personButtonView = UIView()
var buttonPosition = CGPointMake(padding.width * 0.5, padding.height)
let buttonIncrement = buttonSize.width + padding.width
personButtonView.frame.origin = CGPointMake(0,0)
personButtonView.frame.size.width = (buttonSize.width + padding.width) * CGFloat(avatarCount+1)
personButtonView.frame.size.height = (buttonSize.height + 2.0 * padding.height)
for i in 0...(avatarCount-1) {
let button = UIButton(type: .Custom)
button.tag = i
button.frame.size = CGSize(width: 60, height: 55)
button.frame.origin.x = buttonPosition.x
button.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
button.setImage(UIImage(named: self.person[button.tag].avatar), forState: .Normal)
button.addTarget(self, action: "addPersonSelected:", forControlEvents: .TouchUpInside)
buttonPosition.x = buttonPosition.x + buttonIncrement
let label = UILabel(frame: CGRectMake(0, 60, 60, 15))
label.text = self.person[i].name
button.addSubview(label)
personButtonView.addSubview(button)
}
}
personsScrollView.contentSize = personButtonView.frame.size
personsScrollView.addSubview(personButtonView)
return personButtonView
}
This function creates a UIView "personButtonView" that will contain the 10 UIbuttons "button", and each button has a different image.
My questions is, outside of the code, in ViewDidLoad(), how do I access for example to the image of the 2nd button to replace only that image? Or how to access to the label of the 4th button to change its text?
I would appreciate your help!
Thanks in advance!

Looking at this code, a hierarchy of views and subviews is being created. If you want a clean way to access these then you can simply create UIView subclasses to encapsulate your content.
To do this, refactor your code in createPersonsScroll into initializers. I can see several UIButton views being created, so you can define an AvatarButton class to encapsulate those, and probably also a PersonView class for its parent, with a property to access its content.
The idea is that you could then replace
personsScroll = createPersonsScroll(buttonSize: CGSizeMake(60.0, 80.0), avatarCount: 10)
with for example:
personView = PersonView(CGSizeMake(60.0, 80.0), avatarCount: 10)
// ...
personView.avatarButtons[2].image = myImage
personView.avatarButtons[4].text = myString

Related

Why does the selector, #objc, function add a subview, of my button image (as a UIImageView), in my UIButton?

I first declare the button
let balloon = UIButton()
A background image then gets added to the balloon
balloon.setBackgroundImage(UIImage(named:"balloon.jpg"), for: .normal)
An image view of the points get added to the balloon as a subview
subView = UIImageView(image: UIImage(named: "1") )
subView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
subView.contentMode = UIView.ContentMode.scaleAspectFit
subView.center = CGPoint(x: balloon.frame.width/2, y: balloon.frame.height/2)
balloon.addSubview(subView)
I then use the addTarget function for the balloon
balloon.addTarget(self, action: #selector(pop), for: .touchUpInside)
After pop gets called (when the user taps on the balloon), the balloon now contains 2 subviews
- At index 0 of balloon.subviews, there is a UIImageView that is essentially the picture of the balloon with the same dimensions as the balloon button
- And the subs view that I added (aka the points)
here is how I found this problem in my addTarget function (pop):
#objc func pop(_ balloon: UIButton){
print("4.This is the balloon after calling pop \(balloon)")
print("5. This is the subview of the balloon after calling pop \(balloon.subviews)")
Ive added print statements in my function that verifies that the balloons are the same in both the pop func and my balloon creation func
I have already looked at the documentation for both UIButton and addTarget and neither of them have specified why the background image of the button gets created as a subview of the button when the selector func gets called
There shouldn't be that extra UIImageView in my UIButton since I never added that
What you're seeing has nothing to do with your selector / #objc func...
Many UIKit classes do a lot of "under-the-hood" work.
In the case of UIButton, subviews are only added as-needed.
For example, if this is all the code you execute:
let balloon = UIButton()
balloon.frame = CGRect(x: 100, y: 200, width: 240, height: 100)
view.addSubview(balloon)
The resulting button has Zero subviews.
If we do this:
let balloon = UIButton()
balloon.frame = CGRect(x: 100, y: 200, width: 240, height: 100)
view.addSubview(balloon)
balloon.setTitle("ABC", for: [])
The resulting button now has 1 subviews.
let balloon = UIButton()
balloon.frame = CGRect(x: 100, y: 200, width: 240, height: 100)
view.addSubview(balloon)
balloon.setTitle("ABC", for: [])
balloon.setBackgroundImage(img, for: .normal)
The resulting button now has 2 subviews.
let balloon = UIButton()
balloon.frame = CGRect(x: 100, y: 200, width: 240, height: 100)
view.addSubview(balloon)
balloon.setTitle("ABC", for: [])
balloon.setBackgroundImage(imgA, for: .normal)
balloon.addSubview(subview)
The resulting button now has 3 subviews.
let balloon = UIButton()
balloon.frame = CGRect(x: 100, y: 200, width: 240, height: 100)
view.addSubview(balloon)
balloon.setTitle("ABC", for: [])
balloon.setBackgroundImage(imgA, for: .normal)
balloon.addSubview(subview)
balloon.setImage(imgB, for: [])
And now we have 4 subviews.
Apple strongly discourages messing with the internals of UIButton. You might be better off creating a view subclass that contains a button and any additional subviews, rather than your current approach.
Worth Noting
the subviews may not be added until they are needed. So, if you set the title or image or background image in viewDidLoad(), those subviews will not be created until viewDidLayoutSubviews ... or even until the button is actually rendered.
the title label may be created even when you don't expect it to. For example, if the only thing you do to the button is constrain its position, it will get a title label. However, if you set the background image, the title label will be omitted.
Here is a quick example to demonstrate some of the resulting subview counts:
class BtnVC: UIViewController {
var buttons: [UIButton] = []
let stack = UIStackView()
let infoLabel: UILabel = {
let v = UILabel()
v.numberOfLines = 0
v.font = .monospacedSystemFont(ofSize: 14, weight: .regular)
return v
}()
var didLoadStr: String = ""
var didLayoutStr: String = ""
var didAppearStr: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let largeFont = UIFont.systemFont(ofSize: 32)
let configuration = UIImage.SymbolConfiguration(font: largeFont) // <1>
guard let img2 = UIImage(systemName: "02.circle.fill", withConfiguration: configuration),
let img3 = UIImage(systemName: "03.circle.fill"),
let img4 = UIImage(systemName: "04.circle.fill")
else {
return
}
var b: UIButton!
b = UIButton()
buttons.append(b)
b = UIButton()
b.setTitle("1", for: [])
buttons.append(b)
b = UIButton()
b.setTitle("1", for: [])
b.setImage(img2, for: [])
buttons.append(b)
b = UIButton()
b.setTitle("1", for: [])
b.setImage(img2, for: [])
b.setBackgroundImage(img3.withTintColor(.systemGreen, renderingMode: .alwaysOriginal), for: [])
buttons.append(b)
b = UIButton()
b.setTitle("1", for: [])
b.setImage(img2, for: [])
b.setBackgroundImage(img3.withTintColor(.systemGreen, renderingMode: .alwaysOriginal), for: [])
let v = UIImageView(image: img4)
v.tintColor = .systemRed
v.frame = CGRect(x: 0, y: 0, width: 32, height: 32)
b.addSubview(v)
buttons.append(b)
stack.axis = .vertical
stack.spacing = 8
stack.distribution = .fillEqually
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 120.0),
stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
stack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
// add this first button *without* auto-layout
buttons[0].frame = CGRect(x: 80, y: 40, width: 200, height: 50)
view.addSubview(buttons[0])
// add the rest of the buttons to the stack view
buttons.forEach { v in
v.backgroundColor = .systemYellow
v.setTitleColor(.black, for: [])
if v != buttons.first {
stack.addArrangedSubview(v)
}
}
stack.addArrangedSubview(infoLabel)
var s = "didLoad : "
buttons.forEach { v in
s += "\(v.subviews.count) / "
}
didLoadStr += s + "\n"
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// first button is not using auto-layout constraints, so
// let's size it to match the buttons in the stack view
// and posiiton it 8-pts above the stack view
var r = buttons[1].bounds
r.origin.y = stack.frame.origin.y - r.height - 8.0
r.origin.x = stack.frame.origin.x
buttons[0].frame = r
var s = "didLayout: "
buttons.forEach { v in
s += "\(v.subviews.count) / "
}
didLayoutStr += s + "\n"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
var s = "didAppear: "
buttons.forEach { v in
s += "\(v.subviews.count) / "
}
didAppearStr += s + "\n"
infoLabel.text = didLoadStr + didLayoutStr + didAppearStr
}
}
That code will create 5 buttons, with each successive button getting another subview - title, image, backgroundImage, addSubview - and the "Info Label" at the bottom will show the subviews count at each stage (as is often the case, didLayoutSubviews() is called more than once):

How to get a button to hide properly?

In my app, you can increment through an array using the "next" button. Each time you hit next, the next item in the array is displayed in a label. I want another button to hide and unhide depending on which item is in the array/displayed in the label. I tried the code below, but the button is always showing no matter which item is displayed. How do I get the button to hide when the respective item is not showing?
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var quotesLabel: UILabel!
var firstQuote = -1
var quotes = ["The best time to plant a tree was 20 years ago - The next best time is today - Unknown",
"Everytime you spend money, you're casting a vote for the type of world you want - Anna Lappe",
"Buy less, choose well, make it last - Vivienne Westwood",
"The future depends on what we do in the present - Mahatma Gandhi",
]
#IBAction func nextButton(_ sender: Any) {
if firstQuote < quotes.count{
firstQuote = (firstQuote + 1) % quotes.count
quotesLabel.text = quotes[firstQuote]
}
let quote1 = "The best time to plant a tree was 20 years ago - The next best time is today - Unknown"
let quote = quotesLabel.text
let button1 = UIButton(type: .custom)
button1.setImage(UIImage(named: "Heart"), for: .normal)
view.addSubview(button1)
button1.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
button1.isHidden = true
if quote == quote1{
button1.isHidden = false
} else {
button1.isHidden = true
}
}
}
Well, I can't say that I fully understand why this solution to my question worked, but by first putting the button into a view it solved my issue.
let view1 = UIView()
view.addSubview(view1)
view1.frame = CGRect(x: 100, y: 300, width: 200, height: 100)
view1.backgroundColor = .blue
let button1 = UIButton(type: .custom)
button1.setImage(UIImage(named: "Heart"), for: .normal)
view1.addSubview(button1)
button1.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
button1.isHidden = true

“Unrecognized selector sent to instance” in UIView class when calling addTarget

I am trying to create a DropDownMenu class, but when I try to call addTarget to one of the buttons, this error comes up.
unrecognized selector sent to instance 0x7fd167f06120' terminating with uncaught exception of type NSException
I would really appreciate any help and no answer is a bad one!
Here is my entire class
class DropDownMenu: UIView {
// Main button or Pre
var main: UIButton! = UIButton(frame: CGRect(x: 0, y: 0, width: 46, height: 30))
var view: UIView!
// Title
var buttonTitles: [String]! = [""]
var titleColor: UIColor! = UIColor.black
var font: UIFont! = UIFont.systemFont(ofSize: 18)
// Individual Button
var buttonsBorderWidth: CGFloat! = 0
var buttonsBorderColor: UIColor? = UIColor.white
var buttonsCornerRadius: CGFloat! = 0
var color: UIColor! = UIColor.clear
// Button Images
var buttonsImageEdgeInsets: UIEdgeInsets? = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
var images: [UIImage]? = nil
// Onclick stuff
var target: UIViewController!
private var currentSelected: String? = nil
private var optionsStack = UIStackView()
init(main: UIButton) {
self.main = main
super.init(frame: CGRect())
}
func createDropDownMenu() {
main.addTarget(target, action: #selector(DropDownMenu.openDropdown(_:)), for: .touchUpInside)
print("Button Target?: \(main.allTargets), self.target: \(String(describing: target))")
let mainFrame = main.frame
optionsStack.frame = CGRect(x: mainFrame.minX, y: mainFrame.maxY, width: mainFrame.width, height: CGFloat(buttonTitles.count) * mainFrame.height)
optionsStack.axis = .vertical
view.addSubview(optionsStack)
var y: CGFloat! = 0
for title in buttonTitles {
let button = UIButton(frame: CGRect(x: 0, y: y, width: mainFrame.width, height: mainFrame.height))
button.setTitle(title, for: .normal)
button.setTitleColor(titleColor, for: .normal)
button.backgroundColor = color
button.titleLabel?.font = font
button.addTarget(target, action: #selector(DropDownMenu.onclick), for: .touchUpInside)
y += mainFrame.height
optionsStack.addArrangedSubview(button)
}
for button in optionsStack.arrangedSubviews {
button.isHidden = true
button.alpha = 0
}
}
#objc private func openDropdown(_ sender: UIButton) {
print("sender: \(String(describing: sender))")
optionsStack.arrangedSubviews.forEach { (button) in
UIView.animate(withDuration: 0.7) {
button.isHidden = !button.isHidden
button.alpha = button.alpha == 0 ? 1 : 0
self.view.layoutIfNeeded()
}
}
}
#objc private func onclick(_ sender: UIButton) {
let title = sender.titleLabel!.text
print(title as Any)
main.setTitle(title, for: .normal)
optionsStack.arrangedSubviews.forEach { (button) in
UIView.animate(withDuration: 0.7) {
button.isHidden = true
button.alpha = 0
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is the code and creation of the object in ViewController
let grade = UIButton(frame: CGRect(x: 50, y: 300, width: 80, height: 30))
grade.layer.borderWidth = 1
grade.setTitle("Grade", for: .normal)
grade.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
grade.setTitleColor(UIColor.black, for: .normal)
let gradeDP = DropDownMenu(main: main)
gradeDP.buttonTitles = ["Title 1", "Title 2", "Title 3"]
gradeDP.color = UIColor.gray
gradeDP.target = self
gradeDP.titleColor = UIColor.white
gradeDP.view = view
view.addSubview(grade)
gradeDP.createDropDownMenu()
The first print statement in the createDropDownMenu() function prints...
Button Target?: [AnyHashable(<HomeworkHelp.DropDownMenu: 0x7ffb555200b0; frame = (0 0; 0 0); layer = <CALayer: 0x600002bdf5c0>>)], self.target: Optional(<HomeworkHelp.CreateAccountViewController: 0x7ffb5550a7b0>)
After editing it with the help of mightknow I came up with this class. It doesn't have any onclick actions for the mainButton in it.
class DropDownMenu: UIStackView {
var options: [String]! = [] // Labels for all of the options
var titleButton: UIButton! = UIButton() // The Main Title Button
init(options: [String]) {
self.options = options
let mainFrame = titleButton.frame
super.init(frame: CGRect(x: mainFrame.minX, y: mainFrame.maxY, width: mainFrame.width, height: mainFrame.height * CGFloat(options.count)))
var y: CGFloat = 0
for title in self.options {
let button = UIButton(frame: CGRect(x: 0, y: y, width: self.frame.width, height: mainFrame.height))
button.setTitle(title, for: .normal)
button.setTitleColor(titleButton.titleLabel?.textColor, for: .normal)
button.backgroundColor = titleButton.backgroundColor
button.addTarget(self, action: #selector(dropDownOptionClicked(_:)), for: .touchUpInside)
button.isHidden = true
button.alpha = 0
self.addArrangedSubview(button)
y += 1
}
}
#objc func openDropDown(_ sender: UIButton) {
print("Open DropDownMenu")
for button in self.arrangedSubviews {
UIView.animate(withDuration: 0.7) {
button.isHidden = !button.isHidden
button.alpha = button.alpha == 0 ? 1 : 0
self.layoutIfNeeded()
self.superview?.layoutIfNeeded()
}
}
}
#objc private func dropDownOptionClicked(_ sender: UIButton) {
print(sender.titleLabel?.text as Any)
}
init() {
super.init(frame: CGRect.zero)
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And than my ViewController is ...
let dp = DropDownMenu(options: ["Label 1", "Label 2", "Label 3"])
let titleButton = UIButton(frame: CGRect(x: 100, y: 300, width: 180, height: 40))
titleButton.backgroundColor = UIColor.white
titleButton.setTitle("DropDownMenu", for: .normal)
titleButton.setTitleColor(UIColor.black, for: .normal)
titleButton.layer.borderWidth = 2
titleButton.addTarget(self, action: #selector(dp.openDropDown(_:)), for: .touchUpInside)
dp.titleButton = titleButton
The error ...
Button Target?: [AnyHashable(<HomeworkHelp.DropDownMenu: 0x7ffb555200b0; frame = (0 0; 0 0); layer = <CALayer: 0x600002bdf5c0>>)], self.target: Optional(<HomeworkHelp.CreateAccountViewController: 0x7ffb5550a7b0>)
still comes up and I am clueless as to why.
You're setting the target as a UIViewController when the method you're calling is actually a method of the DropDownMenu class. What you need to do is set the target to self instead of the target property:
main.addTarget(self, action: #selector(DropDownMenu.openDropdown(_:)), for: .touchUpInside)
EDIT: In response to your comment, here is the code I'm using to test it. There are some layout/color choices I made just to make it clear to me, but this works:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let main = UIButton(frame: CGRect(x: 0, y: 100, width: 80, height: 30))
main.layer.borderWidth = 1
main.setTitle("Grade", for: .normal)
main.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
main.setTitleColor(UIColor.black, for: .normal)
let gradeDP = DropDownMenu(main: main)
gradeDP.buttonTitles = ["Title 1", "Title 2", "Title 3"]
gradeDP.color = UIColor.gray
gradeDP.target = self
gradeDP.titleColor = UIColor.white
gradeDP.view = UIView()
self.view.addSubview(gradeDP)
let b = self.view.bounds
gradeDP.frame = CGRect(x: b.minX, y: b.minY, width: b.width, height: b.height/2)
gradeDP.backgroundColor = UIColor.purple
gradeDP.target = self
gradeDP.addSubview(gradeDP.main)
gradeDP.createDropDownMenu()
}}
As for your code, I'm going on the assumption that the code you added in the second part of your question is inside your ViewController's viewDidLoad() method, and that the main variable you're using to initialize your DropDownMenu is an instance variable of your ViewController, because I'm not seeing it anywhere else in scope. If that's the case, there are definitely some issues. They are:
You never actually add gradeDP to your view hierarchy. If that's what the line gradeDP.view = view is supposed to do, it's not. What that code actually does is set the view property of gradeDP to be a reference to the ViewController's view property. And, unless there is code in your DropDownMenu class that you haven't included, you're not actually using that reference for anything. So, you can get rid of that line entirely, and the view property in your DropDownMenu class. If what you're trying to do is set the ViewController's view to be gradeDP, that code would be self.view = gradeDP, but I don't actually recommend doing it that way. A UIViewController's view property is used in some special functionality and probably shouldn't be messed with much. You probably want to add gradeDP as a subview, like I did in my code above.
The grade button you created is not used by your DropDownMenu. I'm guessing you meant to initialize with that instead of the main variable (that is out of scope of your code), like this:
let gradeDP = DropDownMenu(main: grade)
In short, unless there is code elsewhere that you haven't shared, what your code above does is create a UIButton labeled "Grade" that is visible but doesn't actually do anything (and isn't part of your DropDownMenu), and a DropDownMenu that isn't actually visible, but would have a main button that calls openDropdown(_:) if it was. I'm guessing that's not how it's supposed to work. Hopefully the code I provided above helps get you where you want to be, though.
As for suggestions with rebuilding your class so it works properly, you may want to start with something like this:
class DropDownMenu : UIView {
var dropdownOptions : [String] = []
private var titleButton : UIButton = UIButton()
private var optionsStack : UIStackView = UIStackView()
private var optionsButtons : [UIButton] = []
#objc private func openDropdown(_ sender: UIButton) {
// Add code to make dropdown options appear. There are multiple ways of doing this. For instance, the optionsButtons could be hidden and then unhidden when it's clicked, or they could be created only once the button is clicked.
}
#objc private func selectedOption(_ sender: UIButton) {
// Code here for when option is selected
}
init(options: [String]) {
self.dropdownOptions = options
super.init(frame: CGRect.zero)
// Customize all of your subviews here, and add them to your DropDownMenu (as subviews)
// Add openDropdown(_:) target to self.titleButton
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
A lot of the code you have already written for your original version of the class can go inside the functions there. Also, there is a lot of unnecessary code in your original version. For example, the target variable is unused once you fixed the original error issue, the view variable is obsolete, and the createDropDownMenu() function is unnecessary because all of that code can go either in the init(options:) or openDropdown(_:) functions.
Then, if you choose to build out a class using that template, you would implement it in your ViewController's viewDidLoad() method with the code:
let dropdown = DropDownMenu(titles: ["Title 1", "Title 2", "Title 3"])
self.view.addSubview(dropdown)
// Follow that with layout code that ensures it's the proper size and in the proper location
I hope that combined with my comments make sense, are helpful, and aren't too overwhelming. What I recommend doing is starting a new empty project (or target) and building your class and adding it to a ViewController with nothing else in it. That's a good way to isolate it and check and make sure everything looks and works right. In case you want an alternate suggestion with how to build your class, you can actually try making DropDownMenu be a subclass of UIStackView (instead of UIView) with the main button and all option buttons being arranged subviews. This might actually be simpler, because it kind of cuts out the middleman, if you will, and all you'd need to do when opening/closing the dropdown is add/remove views from the .arrangedSubviews property.
Also important is that if your view needs to pass information (such as which option is selected) back to the ViewController, make sure the reference to the ViewController is marked weak so you don't create a retain cycle.
On a final note, if you're disappointed that there isn't a quick fix to get the original class to work and want to keep trying at that, there might be some way to cobble together a solution (like the code from my first answer, which does actually work...), but ultimately it will probably only cause more issues further down the line. So, best of luck with everything.
I finally figured it out! The target has to be the DropDownMenu.
titleButton.addTarget(dp, action: #selector(dp.openDropDown(_:)), for: .touchUpInside)
Here is the rest of the code...
let titleButton = UIButton(frame: CGRect(x: 50, y: 290, width: 100, height: 40))
titleButton.backgroundColor = UIColor.white
titleButton.setTitle("Grade", for: .normal)
titleButton.setTitleColor(UIColor.black, for: .normal)
titleButton.layer.borderWidth = 2
titleButton.layer.cornerRadius = 10
let dp = DropDownMenu(options: ["1", "Freshman", "Sophomore", "Junior", "Senior", "College"])
dp.titleButton = titleButton
dp.target = self
dp.borderWidth = 2
dp.spacing = 5
dp.cornerRadius = 10
dp.bgColor = UIColor.white
Adding it to subviews and creating it...
view.addSubview(titleButton)
view.addSubview(dp)
dp.createDropDownMenu()

change color of uiimageview in a empty array by pressing on it

My swift code below has a button and when touch places a imageview on the viewcontroller. The only thing I want to do is when a individual imageview not all of the imageview just the one select is touch I would like to change the color from purple to blue only if touched and one at a time.
import UIKit
class ViewController: UIViewController {
var count: Int = 0
var ht = -90
var ww = 80
var moveCounter = 0
var counter = 0
var arrTextFields = [UIImageView]()
var b7 = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[b7].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
b7.frame = CGRect(x: view.center.x-115, y: view.center.y + 200, width: 70, height: 40)
b7.addTarget(self, action: #selector(addBOx), for: .touchUpInside)
}
//func that adds imageview.
#objc func addBOx() {
let subview = UIImageView()
subview.isUserInteractionEnabled = true
arrTextFields.append(subview)
view.addSubview(subview)
subview.frame = CGRect(x: view.bounds.midX - 0, y: view.bounds.midY + CGFloat(ht), width: CGFloat(ww), height: 35)
subview.backgroundColor = .purple
subview.tag = count
count += 1
ht += 50
arrTextFields.append(subview)
}}
You already set the isUserInteractionEnabled property of the image view to true. To respond to touch events, create a UITapGestureRecognizer and add it to the image view like so:
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
subview.addGestureRecognizer(gestureRecognizer)
Then inside of the imageViewTapped method you can extract the image view from the sender argument:
#objc func imageViewTapped(sender: UITapGestureRecognizer) {
if let imageView = sender.view as? UIImageView {
// Change the imageView's background here
}
}

Creating dynamic multiple UIButton programmatically

I am creating my UI programmatically, and this is how I create a button
let button: UIButton = {
let button = UIButton()
button.setTitle("Try1",.normal)
return button
}
And this is what I want to do. For example movie1 has got 2 categories, Dram & Horror I just want to show 2 of them.
Movie2 has got 4 categories, Dram&Horror&Sport& War i just want to show 4 of them.
And now Im stuck I have NO idea what to do. I know you guys gonna give minus rep but I need help.
Done! Solution is below
First I have made a struct
struct Category {
let title: String
let color: UIColor
init(title: String, color: UIColor) {
self.title = title
self.color = color
}
}
Then I made an empty array type of Category Struct
var categories = [Category]()
/* I appended some stuff manually to this array */
categories.append(Category(title: "Dram", color: Colors.cherry))
categories.append(Category(title: "Sport", color: Colors.lightGreen))
categories.append(Category(title: "Horror", color: Colors.purple))
categories.append(Category(title: "War", color: Colors.darkBlue))
/* Then I made a for loop */
for category in categories{
let button = UIButton()
button.createButton(title: category.title, titleColor: .white, fontType: "bold", fontSize: 17, background: category.color, cornerRadius: 4)
stackView.addArrangedSubview(button)
}
Use Horizontal StackView for that. In StackView you can add as much arranged subviews as you want and it will be automatically adjusting to the available space. Just setup constraints for StackView and rest will just work out of the box.
You can add your button to StackView using:
stackView.addArangedSubview(button)
To create button you can simple use for loop:
for i in 1...3 { //change 3 to your value
let button = UIButton()
stackView.addArangedSubview(button)
}
If you want to create multiple buttons in horizontal then add button in scrollview
Swift 4.0
func ButtonCreation() {
yourScrollviewname.isScrollEnabled = true
yourScrollviewname.isUserInteractionEnabled = true
let numberOfButtons = 16
let numberofRows = 1
var count = 0
var px = 0
var py = 0
for _ in 1...numberofRows {
px = 0
if count < numberOfButtons {
for j in 1...numberOfButtons{
count += 1
let Button = UIButton()
Button.tag = count
Button.frame = CGRect(x: px, y: 0, width: 80, height: 27)
Button.layer.borderWidth = 1.0
Button.layer.masksToBounds = true
Button.setTitleColor(UIColor.black, for: .normal)
print(colorArr[j-1])
Button.setTitle(colorText[j-1], for: .normal)
Button.addTarget(self, action: #selector(scrollButtonAction), for: .touchUpInside)
yourScrollviewname.addSubview(Button)
px = px + Int(yourScrollviewname.frame.width)/2 - 50
}
}else{
for j in numberOfButtons/2...numberOfButtons {
count += 1
let Button = UIButton()
Button.tag = count
Button.frame = CGRect(x: px+10, y: py+10, width: 80, height: 427)
Button.backgroundColor = UIColor.black
Button.setTitle("Hello \(j) ", for: .normal)
Button.addTarget(self, action: #selector(scrollButtonAction), for: .touchUpInside)
yourScrollviewname.addSubview(Button)
px = px + Int(yourScrollviewname.frame.width)/2 - 30
}
}
py = Int(yourScrollviewname.frame.height)-70
}
yourScrollviewname.contentSize = CGSize(width: px, height: py)
}
Swift 3.0 ...
let stackview = UIStackView(frame: CGRect(x: 0, y: 0, width: 512, height: 356)) // set your postion
stackview.axis = . Horizontal
stackview.spacing = 10.0
stackview.alignment = .Fill
stackview.distribution = .EqualSpacing
for i in 1...3 {
let button = UIButton()
stackview.addArangedSubview(button)
}