import UIKit
import SpriteKit
import GameplayKit
struct Question {
var Question : String!
var Answers : [String]!
var AnswerNumber : Int!
}
class LevelOne: SKScene {
var button = Button()
var buttons = [Button]()
let Question_met = UILabel(frame: CGRect(x: 0.3, y: 0.3, width: 40, height: 21))
var Questions = [Question]()
var QNumber = Int()
var buttonNames = [""]
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
QuestionFunction()
ButtonFuction()
PickQuestion()
}
#objc func QuestionFunction(){
Questions = [Question(Question: "What is a Car ?", Answers: ["Bat","Cat","Vehicle","Pat"], AnswerNumber: 3),
Question(Question: "What is a plane ?", Answers: ["Bat","Aircraft","Cat","Pat"], AnswerNumber: 2),
Question(Question: "What is a ant ?", Answers: ["Bat","Cat","Vegetable","Insect"], AnswerNumber: 4),
Question(Question: "What is a Apple ?", Answers: ["Bat","Cat","Fruit","Pat"], AnswerNumber: 3),]
Question_met.frame = CGRect(x: 330, y: 70, width: 200, height: 21)
Question_met.backgroundColor = UIColor.orange
Question_met.textColor = UIColor.white
Question_met.textAlignment = NSTextAlignment.center
Question_met.text = "What caused Global Warming ?"
self.view?.addSubview(Question_met)
}
#objc func ButtonFuction(){
let stacView = UIStackView()
stacView.spacing = 12
stacView.distribution = .fillEqually
stacView.axis = .horizontal
stacView.translatesAutoresizingMaskIntoConstraints = false
view!.addSubview(stacView)
buttonNames = ["One","Two","Three","Four"]
for name in buttonNames {
button = Button()
button.setTitle(name, for: .normal)
stacView.addArrangedSubview(button)
buttons.append(button)
}
NSLayoutConstraint.activate([stacView.centerXAnchor.constraint(equalTo: view!.centerXAnchor),stacView.centerYAnchor.constraint(equalTo: view!.centerYAnchor),stacView.widthAnchor.constraint(equalToConstant: 350),stacView.heightAnchor.constraint(equalToConstant:70)])
}
#objc func PickQuestion(){
if Questions.count > 0{
QNumber = 0
Question_met.text = Questions[QNumber].Question
for i in 0..<buttons.count{
buttons[i].setTitle(Questions[QNumber].Answers[i], for: .normal)
}
Questions.remove(at: QNumber)
}
else {
print("Done!")
}
}
#objc func handleButton() {
print("")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
I created a quiz based game and programmed my button manually but not sure how to create methods that would handle when the button is pressed.
Not sure entirely what you are asking for so going to state what I think you are asking.
You are simply asking on how to connect a button to action.
You're button is declared button = UIButton() in the loop then the action would be handleButton method declared below.
You can simply do addTarget(:) for the button.
button.addTarget(self, action:#selector(handleButton), for: .touchUpInside)
You can google Swift Classname, i.e. Swift UIButton to determine the methods for that class. For example inside UIButton, we can see there is a method identified as addTarget(_:action:for).
From Apple's Docs:
You connect a button to your action method using the addTarget(_:action:for:) method or by creating a connection in Interface Builder. The signature of an action method takes one of three forms, which are listed in Listing 1. Choose the form that provides the information that you need to respond to the button tap.
Edit:
OP decided to ask how to assign button functionality within a loop. To do this, you need a list of functions aka Selectors that you can reference.
let selectors:[Selector] = [#selector(handleButton1), #selector(handleButton2, #selector(handleButton3)]
for i in 0..<3 {
button.addTarget(self, action: selectors[i], for: .touchUpInside)
}
Related
// Button class
class Button: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup(){
backgroundColor = #colorLiteral(red: 0.1215686277, green: 0.01176470611, blue: 0.4235294163, alpha: 1)
layer.cornerRadius = 12
layer.masksToBounds = false
translatesAutoresizingMaskIntoConstraints = false
}
}
// The Class where button object is being created
struct Question {
var Question : String!
var Answers : [String]!
var AnswerNumber : Int!
}
class LevelOne: SKScene {
var button = Button()
var buttons = [Button]()
let Question_met = UILabel(frame: CGRect(x: 0.3, y: 0.3, width: 40, height: 21))
var Questions = [Question]()
var QNumber = Int()
var buttonNames = [""]
var AnsNumber = Int()
let selectors:[Selector] = [#selector(handleButton1), #selector(handleButton2), #selector(handleButton3),#selector(handleButton4)]
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
questionFunction()
buttonFuction()
pickQuestion()
}
#objc func questionFunction(){
Questions = [Question(Question: "What is a BMW ?", Answers: ["Bat","Cat","Vehicle","Pat"], AnswerNumber: 2),
Question(Question: "What is a Boeing ?", Answers: ["Bat","Aircraft","Cat","Pat"], AnswerNumber: 1),
Question(Question: "What is a Ant ?", Answers: ["Insect","Cat","Insect","Nate"], AnswerNumber: 2),
Question(Question: "What is a Apple ?", Answers: ["Fruit","Cat","bat","Pat"], AnswerNumber: 0),
Question(Question: "Where is london ?", Answers: ["UK","USA","France","Germany"], AnswerNumber: 0),
Question(Question: "Where is New York ?", Answers: ["Canada","USA","France","Germany"], AnswerNumber: 2),
Question(Question: "where is Berlin ?", Answers: ["UK","USA","Italy","Germany "], AnswerNumber: 3),
Question(Question: "Where is Toronto ?", Answers: ["India","Africa","Canada","Norway"], AnswerNumber: 2),
Question(Question: "Where is Rome ?", Answers: ["Japan","China","Italy","Ireland"], AnswerNumber: 2),
Question(Question: "where is Chennai ?", Answers: ["India","Brazil","Swiss","Germany"], AnswerNumber: 0),]
Question_met.frame = CGRect(x: 330, y: 70, width: 200, height: 21)
Question_met.backgroundColor = UIColor.orange
Question_met.textColor = UIColor.white
Question_met.textAlignment = NSTextAlignment.center
Question_met.text = "What caused Global Warming ?"
self.view?.addSubview(Question_met)
}
#objc func buttonFuction(){
let stacView = UIStackView()
stacView.spacing = 12
stacView.distribution = .fillEqually
stacView.axis = .horizontal
stacView.translatesAutoresizingMaskIntoConstraints = false
view!.addSubview(stacView)
buttonNames = ["One","Two","Three","Four"]
for i in 0..<4{
button = Button()
button.setTitle(buttonNames[i], for: .normal)
stacView.addArrangedSubview(button)
buttons.append(button)
button.addTarget(self, action: selectors[i], for: .touchUpInside)
}
NSLayoutConstraint.activate([stacView.centerXAnchor.constraint(equalTo: view!.centerXAnchor),stacView.centerYAnchor.constraint(equalTo: view!.centerYAnchor),stacView.widthAnchor.constraint(equalToConstant: 350),stacView.heightAnchor.constraint(equalToConstant:70)])
}
#objc func pickQuestion(){
if Questions.count > 0{
QNumber = 0
Question_met.text = Questions[QNumber].Question
AnsNumber = Questions[QNumber].AnswerNumber
for i in 0..<buttons.count{
buttons[i].setTitle(Questions[QNumber].Answers[i], for: .normal)
}
Questions.remove(at: QNumber)
}
else {
print("Done!")
let sceneToMoveTo = Instructions(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
#objc func handleButton1() {
if AnsNumber == 0 {
pickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
#objc func handleButton2(){
if AnsNumber == 1 {
pickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
#objc func handleButton3(){
if AnsNumber == 2 {
pickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
#objc func handleButton4(){
if AnsNumber == 3 {
pickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
I currently developing a game, which contains various levels and menu scenes. The current level created a question label and answer buttons and even after the level is complete and you navigate to another scene the buttons and questions remain on the scene no matter which scene you navigate too.
I was hoping to get a solution to only have the buttons and label to appear on the designated scene and don't appear in other scenes unless stated.
Thank you in advance.
I don't think you understand the ownership model here. The ViewController owns its view. The view owns the SKView and the SKView owns the SKScene. There is a back pointer from the SKScene to its SKView, and you are using that to add a UIKit StakcView to the Scene's parent view. Since UIKit sits on top of the SKScene, your button is always visible. The correct architecture here is that the view should add and manage its own subviews. Whenever it loads a new SKScene it should determine what UI that scene needs and then hide any UI that should no be shown. You can do this by just setting .isHidden on the relevant views.
struct Question {
var Question : String!
var Answers : [String]!
var AnswerNumber : Int!
}
class LevelOne: SKScene {
var button = Button()
var buttons = [Button]()
let Question_met = UILabel(frame: CGRect(x: 0.3, y: 0.3, width: 40, height: 21))
var Questions = [Question]()
var QNumber = Int()
var buttonNames = [""]
var AnsNumber = Int()
let selectors:[Selector] = [#selector(handleButton1), #selector(handleButton2), #selector(handleButton3),#selector(handleButton4)]
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
QuestionFunction()
ButtonFuction()
PickQuestion()
}
#objc func QuestionFunction(){
Questions = [Question(Question: "What is a BMW ?", Answers: ["Bat","Cat","Vehicle","Pat"], AnswerNumber: 2),
Question(Question: "What is a Boeing ?", Answers: ["Bat","Aircraft","Cat","Pat"], AnswerNumber: 1),
Question(Question: "What is a Ant ?", Answers: ["Insect","Cat","Insect","Nate"], AnswerNumber: 2),
Question(Question: "What is a Apple ?", Answers: ["Fruit","Cat","bat","Pat"], AnswerNumber: 0),
Question(Question: "Where is london ?", Answers: ["UK","USA","France","Germany"], AnswerNumber: 0),
Question(Question: "Where is New York ?", Answers: ["Canada","USA","France","Germany"], AnswerNumber: 2),
Question(Question: "where is Berlin ?", Answers: ["UK","USA","Italy","Germany "], AnswerNumber: 3),
Question(Question: "Where is Toronto ?", Answers: ["India","Africa","Canada","Norway"], AnswerNumber: 2),
Question(Question: "Where is Rome ?", Answers: ["Japan","China","Italy","Ireland"], AnswerNumber: 2),
Question(Question: "where is Chennai ?", Answers: ["India","Brazil","Swiss","Germany"], AnswerNumber: 0),]
Question_met.frame = CGRect(x: 330, y: 70, width: 200, height: 21)
Question_met.backgroundColor = UIColor.orange
Question_met.textColor = UIColor.white
Question_met.textAlignment = NSTextAlignment.center
Question_met.text = "What caused Global Warming ?"
self.view?.addSubview(Question_met)
}
#objc func ButtonFuction(){
let stacView = UIStackView()
stacView.spacing = 12
stacView.distribution = .fillEqually
stacView.axis = .horizontal
stacView.translatesAutoresizingMaskIntoConstraints = false
view!.addSubview(stacView)
buttonNames = ["One","Two","Three","Four"]
for name in buttonNames {
button = Button()
button.setTitle(name, for: .normal)
stacView.addArrangedSubview(button)
buttons.append(button)
}
for i in 0..<4{
button.addTarget(self, action: selectors[i], for: .touchUpInside)
}
NSLayoutConstraint.activate([stacView.centerXAnchor.constraint(equalTo: view!.centerXAnchor),stacView.centerYAnchor.constraint(equalTo: view!.centerYAnchor),stacView.widthAnchor.constraint(equalToConstant: 350),stacView.heightAnchor.constraint(equalToConstant:70)])
}
#objc func PickQuestion(){
if Questions.count > 0{
QNumber = 0
Question_met.text = Questions[QNumber].Question
AnsNumber = Questions[QNumber].AnswerNumber
for i in 0..<buttons.count{
buttons[i].setTitle(Questions[QNumber].Answers[i], for: .normal)
}
Questions.remove(at: QNumber)
}
else {
print("Done!")
}
}
#objc func handleButton1() {
if AnsNumber == 0 {
PickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
#objc func handleButton2(){
if AnsNumber == 1 {
PickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
#objc func handleButton3(){
if AnsNumber == 2 {
PickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
#objc func handleButton4(){
if AnsNumber == 3 {
PickQuestion()
print("Correct!")
}
else {
print("You are Wrong!!!")
}
}
}
The button action methods don't seem to be accurate in knowing which right answer button is being pressed while different number is assigned as answer for each question the methods seems to only move forward when pressing the last button.
Thank you in advance much appreciate the help.
Your problem is located in your ButtonFunction():
for name in buttonNames {
button = Button()
button.setTitle(name, for: .normal)
stacView.addArrangedSubview(button)
buttons.append(button)
}
for i in 0..<4{
button.addTarget(self, action: selectors[i], for: .touchUpInside)
}
In the second loop, you do not initialize the button variable, so this is always the fourth button..
Better join this code in only one loop like so:
for i in 0..<4{
button = Button()
button.setTitle(buttonNames[i], for: .normal)
stacView.addArrangedSubview(button)
buttons.append(button)
button.addTarget(self, action: selectors[i], for: .touchUpInside)
}
Beyond this solution some advices to your coding style:
for convenience and better readability, function names and variable names should start with a lower letter
declare your variables in the smallest possible scope.
button, buttonNames and selectors are only used in ButtonFunction(), so these should be declared there.
declaration of button should in fact be in the for loop only. This way, the compiler would have told you, that button is unknown in your second loop and you would have seen the problem on your own ;)
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()
I want my swift code to use a if statement or another sequence to only display a border on one of the buttons if click at a time. So a border can only be seen on one button at a time that button would be the last one pressed. I know I could say layer.border with 0 on each button that should be selected but I want to see if there is a more efficient way to do this.
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
ba.addTarget(self, action: #selector(pressa), for: .touchDown)
bb.addTarget(self, action: #selector(pressb), for: .touchDown)
bc.addTarget(self, action: #selector(pressc), for: .touchDown)
}
#objc func pressa(){
ba.layer.borderWidth = 2
}
#objc func pressb(){
bb.layer.borderWidth = 2
}
#objc func pressc(){
bc.layer.borderWidth = 2
}
}
you can add target to all buttons at the forEach and be only one method as #Sh_Khan mention
import UIKit
class ViewController: UIViewController {
var ba = UIButton()
var bb = UIButton()
var bc = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[ba,bb,bc].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown)
}
ba.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
bb.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
bc.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
ba.backgroundColor = .blue
bb.backgroundColor = .orange
bc.backgroundColor = .darkGray
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}
}
See how you have used an array in [ba,bb,bc].forEach { ... } to reduce code duplication? Using arrays is the key. Rather than putting the three buttons in an array inline like that, create a property instead:
var buttons: [UIButton]!
override func viewDidLoad() {
super.viewDidLoad()
buttons = [ba,bb,bc]
buttons.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.addTarget(self, action: #selector(buttonPressed), for: .touchDown)
}
...
}
I have used the same selector buttonPressed for all three buttons. buttonPressed can accept a parameter of type UIButton, that tells us which button is pressed:
#objc func buttonPressed(_ sender: UIButton) {
buttons.forEach { ba.layer.borderWidth = 0 } // deselect all buttons first...
sender.layer.borderWidth = 2 // select the tapped button
}
If you have more than 3 buttons to manage, I suggest you don't use UIButtons at all. You should use a UICollectionView. (Learn how to use them) This view will handle the selection for you. It also allows scrolling when there's not enough space to show all the buttons. You just need to create a custom UICollectionViewCell and override its isSelected property:
override var isSelected: Bool {
didSet {
if isSelected {
self.layer.borderWidth = 2
} else {
self.layer.borderWidth = 0
}
}
}
It could be 1 method like this
[ba,bb,bc].forEach { $0.addTarget(self, action: #selector(pressAll(_:)), for: .touchDown) }
}
#objc func pressAll(_ sender:UIButton) {
[ba,bb,bc].forEach { $0.layer.borderWidth = 0 } // reset all
sender.layer.borderWidth = 2
}
I am trying to center a Button onto the bottom of a view but it never appears. The only time it appears is when I uncomment takePhotoButton.frame. What is the proper way to do this?
import UIKit
import AVFoundation
class InputViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let photoPreviewImageView = UIImageView()
photoPreviewImageView.frame = view.bounds
photoPreviewImageView.backgroundColor = UIColor.green
view.addSubview(photoPreviewImageView)
let imageOfPhotoButton = UIImage(named: "smallcircle.circle.fill") as UIImage?
let takePhotoButton = UIButton(type: .custom) as UIButton
takePhotoButton.setImage(imageOfPhotoButton, for: .normal)
//takePhotoButton.frame = CGRect(x: 10, y: 10, width: 60, height: 60) // It will appear with this code however i took it away because im trying to center it at the bottom of the screen
takePhotoButton.center = view.center
photoPreviewImageView.addSubview(takePhotoButton)
}
}
Use constraint anchors. After you add the takePhotoButton set them the following way:
takePhotoButton.bottomAnchor.constraint(equalTo: photoPreviewImageView.bottomAnchor).isActive = true
takePhotoButton.centerXAnchor.constraint(equalTo: photoPreviewImageView.centerXAnchor).isActive = true
This will set make your button have the same bottom and center as it's container.
Good day,
you have to add constraint.
import UIKit
class ViewController: UIViewController {
var loginButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Login", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .red
button.tintColor = .white
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
constraintsInit()
}
func constraintsInit(){
view.addSubview(loginButton)
NSLayoutConstraint.activate([
loginButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
loginButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
loginButton.heightAnchor.constraint(equalToConstant: 30),
loginButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,constant: 30),
loginButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,constant: -30),
])
}
}
on youtube you can find several people that explain how create the views, using only code.