I'm in the process of learning Swift and I have a basic question. I don't want to use a Storyboard and I want to create a button with code. At the moment, I code my button this way:
let button1: UIButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
button1.setImage(UIImage(named: "myImage.png"), forState: UIControlState.Normal)
button1.addTarget(self, action: "myMethod", forControlEvents: UIControlEvents.TouchUpInside)
button1.frame = CGRectMake(0, 0, 25, 25) // then I add this button to a navigation control
This works, but I'm looking for something slightly different. That is: I want to create a class, I want to pass at least the image name, the method the button must call and the frame. Moreover, I want to instantiate the button in my ViewController whenever I want.
I found many posts here but no post was really useful (some code did not work).
Can you help me?
enum SPBarButtonTypes {
case openDrawer
case loginInfo
}
class SPBarButtonFactory: NSObject {
class func createBarButtonItemOfType (buttonType: SPBarButtonTypes, withTarget buttonTarget: AnyObject, andAction buttonAction: Selector) -> UIBarButtonItem {
var returnButton: UIBarButtonItem = UIBarButtonItem()
returnButton.target = buttonTarget
returnButton.action = buttonAction
switch buttonType {
case .openDrawer:
returnButton.image = UIImage(named: "ic_menu")
case .loginInfo:
returnButton.image = UIImage(named: "ic_info_outline")
default: NSLog("Wrong BarButton type")
}
return returnButton
}
}
ADDED TO IMPROVE COMMENT
It just "seams" like a class nested in another class, but it really is Swifts way to declare a class-method....
This line declares a class named SPBarButtonFactory
class SPBarButtonFactory...
This line declares a class-method of SPBarButtonFactory
class func createBarButtonItemOfType...
Related
now I practice applying the MVC pattern in my Swift project.
I have one View Controller (VC) file and one UIView file.
In the VC file, I added the UIView file like below.
class MyViewController: UIViewController {
private var myView = MyView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myView)
myView.frame = CGRect(origin: .zero, size: view.bounds.size)
// and other codes...
}
#objc func showDeleteAlert() {
print("showDelete is pressed")
}
}
Then, inside the UIView file, I added some views and buttons (I just copy the button part).
class MyView: UIView {
lazy var deleteButton: UIButton = {
let button = UIButton()
button.setTitle("delete", for: .normal)
button.addTarget(target: MyViewController, action: #selector(showDeleteAlert), for: UIControl.Event.touchUpInside) // -> I get error in here saying "Cannot find 'showDeleteAlert' in scope"
return button
}()
// and more codes...
}
What I want to do here is how to set the target to showDeleteAlert function when the deleteButton is pressed?
I saw tutorials add "self" as a target argument, but in my case, I separated view and controller so not really sure how to access the function in MyViewController.
Thank you...
You have two options here:
move addTarget into view controller viewDidLoad:
myView.deleteButton.addTarget(self, action: #selector(showDeleteAlert), for: UIControl.Event.touchUpInside)
If you wanna make button private(which is a good practice), you can add proxy func to your MyView
func addDeleteButtonTarget(_ target: Any?, action: Selector) {
deleteButton.addTarget(target, action: action, for: .touchUpInside)
}
And call it:
myView.addDeleteButtonTarget(self, action: #selector(showDeleteAlert))
In any case, you cant call button.addTarget(target: MyViewController, ...), because you need to pass an instance of the MyViewController, not just a class name.
I am just learning the swift basics and thought it would be a good idea that I try using my skills and a problem appeared. I have tried everything I know can someone help. My image below.
First of all please post text, not images
You have to use the (IBOutlet) instance number rather than the type UIButton and you have to use the proper API
number.setTitle(String(score), for: .normal)
But in an IBAction I'd declare the method with the static sender type (rather than unspecified Any) and use that
#IBAction func touched(_ sender : UIButton) {
score += 1
sender.setTitle(String(score), for: .normal)
}
If you want to change the title of a button, you need to do this:
button_Outlet_Name.setTitle(title: String?, for: UIControlState)
or
button_Outlet_Name.title.text = "New Title"
Remember to do this on your button OUTLET, not on the UIButton class
You cannot change the your button's title that way, what you have done there by writing
UIButton.title = String(score)
This means you are calling a static method of UIButton class and the name of the method is title.
If you want to change the button's tite you can do that the below way:
Step 1: Take a reference of your button by ctrl+drag.
Step 2: Inside your IBAction you need to write:
yourButton.setTitle("\(score)", for: .normal)
Here you are accessing the static method of the UIButton class. If you want to set the title, you need to do so on the instance.
Based on what you have, within the IBAction, you can cast the sender as a UIButton and then set the title. You’d also do that for a specific state since the titles are closely related to the the state for a UIButton.
if let btn = sender as? UIButton {
btn.setTitle(“\(score)”, forState: .normal)
}
You could have also used the IBOutlet reference instead.
number.setTitle(“\(score)”, forState: .normal)
Whenever you use \(variable) within a string, it uses the string value of the variable to be displayed in the string.
I'm not sure what are u trying to do but if you want to change the title of the clicked button u can do like this:
#IBAction func touched(_ sender: Any) {
score += 1
// check if the sender is UIButton
if let button = sender as? UIButton {
//change your button title
button.setTitle("\(scroe)", for: UIControlState.normal)
}
}
I try figure out why self point to the GameViewController instead of Answer
GameViewController.swift
class GameViewController: UIViewController {
var gameplay = QuestionsController(colors: colors)
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(gameplay.answersController.answers[0].button)
}
func didPressAnswerButton(sender: UIButton!) {
sender.setTitle("Im from GameViewController class", forState: .Normal)
}
}
QuestionsController.swift
class QuestionsController {
var question: Question
var answersController: AnswersController
}
AnswersController.swift
class AnswersController {
var answers = [Answer]()
func prepareAnswers() {
let answer = Answer()
answers.append(answer)
}
}
Answer.swift
class Answer{
let button: UIButton
func prepareButton() {
let answerButton = AnswerButton(type: .System)
answerButton.addTarget(self, action: "didPressAnswerButton:", forControlEvents: .TouchUpInside)
button = answerButton
}
func didPressAnswerButton(sender: UIButton!) {
sender.setTitle("Im from Answer class", forState: .Normal)
}
}
addTarget:action:forControlEvents: tells the control (answerButton in this case) what method to call, and what object to call it on, when the user taps the button. Looking at your code in more detail:
answerButton.addTarget(self, action: "didPressAnswerButton:", forControlEvents: .TouchUpInside)
When the user taps a button, the TouchUpInside event fires on the answerButton, and when that happens we want to invoke a method didPressAnswerButton: on an Answer object
So, we need to tell answerButton what do do when this TouchUpEvent fires. You do this calling the addTarget:action:forControlEvents method on the answerButton
The self argument tells the answerButton what object to notify about the event: it is the target. In this context, self is an Answer object.
The "didPressAnswerButton:" argument indicates what method the answerButton should call in response to the tap event: this is the action
This is the target-action mechanism of Objective-C/Cocoa. It's a very common pattern, it's worth it to read the linked documentation to learn a bit more about how it works. The key is that this is based on Objective-C* message passing: in the code above, "didPressAnswerButton:" indicates a selector, which when paired with a target (self), tells the answerButton how to send a "message" to the target when the user taps the button.
Also, note that when you are editing a storyboard and ctrl-drag from a button to your view controller and select a method, you are also setting up a target/action using this same mechanism. You select the target object by dragging to the view controller icon (or some other icon), and then you pick the action/selector when clicking on a method name in the popup.
* Target-Action was originally designed for Objective-C, but for the common case of implementing a view controller, you can assume Swift works the same way. Just note when reading documentation that Swift uses simple strings for actions, whereas Objective-C uses #selector(...).
How do you addTarget to a UIButton in an NSObject class? Or will that class not work?
class Support: NSObject {
func helpButton(viewController: ProfileVC) {
let helpButton = viewController.helpButton
helpButton.frame.size = CGSizeMake(35.0, 35.0)
helpButton.layer.cornerRadius = helpButton.frame.height/2
helpButton.layer.borderWidth = 0.5
helpButton.layer.borderColor = lightColoredFont.CGColor
helpButton.setTitle("?", forState: .Normal)
helpButton.setTitleColor(lightColoredFont, forState: .Normal)
helpButton.titleLabel?.font = fontSmaller
helpButton.addTarget(self, action: Selector("showOptions"), forControlEvents: .TouchUpInside)
helpButton.center.y = viewController.logoutButton.center.y
helpButton.frame.origin.x = (viewController.view.bounds.width - viewController.logoutButton.frame.maxX)
viewController.view.addSubview(helpButton)
}
func showOptions() {
print("showing")
}
}
The print is not showing. Even if I feed an instantiated support class into the target for the button it will not work. What is the proper way to do this?
In short, no.
NSObject does not inherit from anything in UIKit. Your inheritance should be the other way around. Perhaps you could make a UIButton that has a property of type NSObject to carry some accompanying information?
Look at the UIControl API
func addTarget(_ target: AnyObject?,
action action: Selector,
forControlEvents controlEvents: UIControlEvents)
But what are you trying to do? Normally you would add a button in interface builder and a reference (IBOutlet) to that button from some controller class like UIViewController.
edit
Ah, now I see the problem. Don't use Selector in swift.
This should work.
helpButton.addTarget(self, action: "showOptions", forControlEvents: .TouchUpInside)
I am trying to create a custom drop down list in a ViewController. There are going to be 5 drop down lists and each list will have 4 options. Because of the number of lists, I decided to make a UIView that has the four choices in the form of UIButtons for each of the lists. Right now I am just trying to get one down; therefore, the following code is for ONE drop down list with FIVE options (including the one selected, which I will explain further below).
Essentially what I want is to have a button showing the selected value (or a default value at launch) and then when you click on that value then the UIView that contains 4 buttons (aka the drop down list) is shown below the original button. When the user clicks on one of the buttons I want the the button with the selected value to have the title of the button that was clicked on.
I am having the following issues:
I want to be able to pass the titles of the four buttons from the ViewController to the UIView because I want to use this UIView multiple times with different values for the titles of the four buttons. I don't know how to pass values to a UIView class.
When a choice from the drop down list (ie a UIButton) is clicked I can't figure out how to pass the value of the title of the button from the UIView back to UIViewController. I tried setting the title to a variable in the ViewController but that didn't work (showed up as nil).
Thank you so much in advance - I know this is a long questions and I am really unsure if this is even a good approach to take for what I am trying to do but it made sense in my head.
Here is my code for the ViewController
var buttonsLeft: buttonsView = buttonsView() // this is the UIView subclass
var time = UIButton.buttonWithType(UIButtonType.System) as! UIButton
override func viewWillAppear(animated: Bool) {
//hidden drop down list
self.buttonsLeft.frame = CGRect(x: self.view.bounds.width*(1/6) - 50, y:120, width:100, height: 135)
self.buttonsLeft.hidden = true
//button with selection showing or the default value at launch
self.time.frame = CGRectMake(self.view.bounds.width * (1/6) - 50, 90, 100, 30)
self.time.setTitle("1 DAY", forState: UIControlState.Normal)
self.time.addTarget(self, action: "showLeft", forControlEvents: UIControlEvents.TouchUpInside)
self.time.hidden = false
self.view.addSubview(self.time)
}
//this function shows the list
func showLeft(){
self.view.addSubview(self.buttonsLeft)
self.buttonsLeft.hidden = false
}
Here is the code for the UIView buttonsView:
import UIKit
class buttonsView: UIView {
var option1 = UIButton()
var option2 = UIButton()
var option3 = UIButton()
var option4 = UIButton()
var buttons: Array<UIButton> = Array()
var title:String = String()
override init(frame: CGRect) {
super.init(frame: frame)
self.buttons = [option1, option2, option3, option4]
self.option1.setTitle("1 DAY", forState: UIControlState.Normal)
self.option2.setTitle("1 MONTH", forState: UIControlState.Normal)
self.option3.setTitle("1 YEAR", forState: UIControlState.Normal)
self.option4.setTitle("LONGER", forState: UIControlState.Normal)
var yStep = 35
for var i:Int = 0; i < 4; ++i {
var totalY:CGFloat = CGFloat(i*yStep)
buttons[i].frame = CGRectMake(0, totalY, 100, 30)
buttons[i].addTarget(self, action: "choseOption:", forControlEvents: UIControlEvents.TouchUpInside)
buttons[i].hidden = false
self.addSubview(buttons[i])
}
}
func choseOption(sender:UIButton){
self.title = sender.titleLabel!.text!
MyView().parentTitle = sender.titleLabel!.text! // my attempt at assigning to variable in View Controller
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Delegation will help you to pass value to UIViewController.
Here are the way you can implement delegate in swift.
Step 1 : Declare protocol in class which is used to sending data. here is buttonsview.
#objc protocol MyButtonDelegate{
optional func didSelectButton(text:String)
}
Step 2 : Now declare delegate in sending class. here is buttonsview.
class buttonsView: UIView {
var delegate:MyButtonDelegate?
[other stuf......]
}
Step 3: now use delegate to send data to 'UIViewController'.
func choseOption(sender:UIButton){
delegate!.didSelectButton(text: sender.titleLabel!.text!)
}
Step 4 : adopt protocol in receiving class.
class ViewController: UIViewController,MyButtonDelegate {
Step 5: implement delegate method in receiving class.
func didSelectButton(text: String) {
parentTitle = "The Buttons title is " + text
}
Step 6: now set delegate
override func viewDidLoad() {
super.viewDidLoad()
buttonsLeft.delegate = self
}
Hope this help you.