Swift / iOS - UIButton Sender with more information - ios

Lets say you have a calculator you're building for iOS in Swift. When a user taps an operation (division, addition, subtraction, etc) I'd like to avoid having a separate method to handle each.
Instead, i'd prefer to have a factory which, based on the sender, determines the correct OperationType sub-class to use (Subtraction, Multiplication, etc)
My question is: is it proper practice to store information directly on the UI Button in Xcode? For instance, on the Addition button (+), if I tagged it with Addition, I could then use that information to load the Addition class.
Similarly, for each digit, i would prefer to have one function which handles a user pushing any digit. However, to determine the sender, it seems somewhat sloppy to use sender.title, since it binds the visual title of the digit, to the code. Is it then appropriate to provide tags to deal with this, or is there another system of handling this?

You can certainly do that, and sometimes it makes for a clean design. View objects have a tag property (an Int.) You can put a switch statement on your action method(s) that switches on the tag value. Generally you should use tag values starting from 1 or greater, since 0 is the default tag value.
If you need more than an integer value you can either save your values into an array that you index into with the tag value or you can use associative storage to actually store objects attached to your buttons. Do a search on "Cocoa associated objects" for more information on that technique. Or you can take a look at a demo project I put on github: https://github.com/DuncanMC/AssocitiveStorageDemo (Written in Objective-C, but the technique is the same in Swift)

override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 200));
let button2 = UIButton(frame: CGRect(x: 0, y: 250, width: 200, height: 200));
button1.backgroundColor = UIColor.greenColor();
button2.backgroundColor = UIColor.yellowColor();
button1.addTarget(self, action: "buttonTaped:", forControlEvents: .TouchUpInside);
button2.addTarget(self, action: "buttonTaped:", forControlEvents: .TouchUpInside);
button1.tag = 0;
button2.tag = 1;
self.view.addSubview(button1);
self.view.addSubview(button2);
}
func buttonTaped(button:UIButton){
switch(button.tag){
case 0:
print("button 1 taped");
break;
case 1:
print("button 2 taped");
break;
default:
break;
}
}

You can use the tag property
func buttonClicked(sender:AnyObject) {
let button = sender as! UIButton
if button.tag == 0 {
...
}
else if button.tag == 1 {
...
}
}

Related

How to programmaticaly add button in iOS game app swift 3?

Can someone explain the code for a programatically added button in an iOS game application for swift 3 Xcode 8? All the other threads on this topic we're in single view and didn't work for me. I couldn't figure out how to add buttons to the game app Main.storyboard, so I'm trying to make a programattically added button. This is the code I'm trying to use now but doesn't work:
var playAgain = UIButton()
playAgain.setTitle("Play Again", for: .normal)
playAgain.setTitleColor(UIColor.black, for: .normal)
playAgain.backgroundColor = SKColor.green
playAgain.layer.borderWidth = 2
playAgain.layer.cornerRadius = 18
playAgain.frame = CGRect(x: (self.frame.width)/2, y: (self.frame.height)/2, width: 100, height: 36)
self.view?.addSubview(playAgain)
Why would the buttons in single view be different in game apps? Also, when(and if) this is created, how would I modify the Touches ended method to know when the button was touched?
Your code is adding the button to the .view, but using the coordinate system of the SKScene. So, your button is there, just not in view.
Assuming you want the button to be centered on the screen (at least, for now), change the placement to:
playAgain.frame = CGRect(x: 0, y: 0, width: 100, height: 36)
playAgain.center = (self.view?.center)!
self.view?.addSubview(playAgain)
This will put the button above the game scene (z-layer, that is), so you can use normal button tap without needing to deal with touches. So, right after you add the button:
self.view?.addSubview(playAgain)
playAgain.addTarget(self, action: #selector(playAgainTapped(_:)), for: .touchUpInside)
and then elsewhere in your class:
func playAgainTapped(_ sender: Any?) -> Void {
print("Play again was Tapped!")
// take whatever action you want here
}
There is another way to create a button programmatically. You can create an empty UIView and override touch method. Also you are able to process touch event on this view and simulate buttons action. I think this is a fastest way for you.

How to navigate a page by using prev and next button like slide show for set of 10 ques?

I'm creating an online tutorial app. In that i want to conduct online test by using set of 10 questions.Could you suggest me in what way i have to design using previous and next button.My condition is,there should be a single Viewcontroller with multiple views(10),the view should change according to prev and next button in the tab bar.
Thanks for consideration.
You have to take all the 10 questions in 1 array & on click of prev next button change the index of array & show the contents in view which contain textview.There is no need of 10 views/Maintain only one view & change contents.Like on Next button increase the counter
i++;
NSString *new = [questionSequenceArray objectAtIndex:i]
questionText.text=new;
As #poojathorat, #Nilesh, #Akshay said you don't need so many viewcontrollers or even that many views. Just have a label and a couple of buttons and have your questions in an array. When the button is clicked change the label text accordingly.
To give you a head start,
// global variables
let qArray = ["Question 1", "Question 2", "Question 3", "Question 4", "Question 5"]
var currIndex = 0
var qLbl: UILabel!
This method will add respective label and buttons
func addQuestionView() {
let qView = UIView(frame: self.view.bounds)
qView.backgroundColor = UIColor.groupTableViewBackgroundColor()
qLbl = UILabel(frame: CGRect(x: 0, y: 0, width: qView.frame.size.width, height: qView.frame.size.height/2))
qLbl.text = qArray[currIndex]
qLbl.textAlignment = .Center
let prevBtn = UIButton(frame: CGRect(x: 0, y: qView.frame.size.height-50, width: qView.frame.size.width/2, height: 50))
prevBtn.tag = 15
prevBtn.setTitle("Prev", forState: .Normal)
prevBtn.backgroundColor = UIColor.darkGrayColor()
prevBtn.addTarget(self, action: "prevBtnAction", forControlEvents: UIControlEvents.TouchUpInside)
let nextBtn = UIButton(frame: CGRect(x: qView.frame.size.width/2, y: qView.frame.size.height-50, width: qView.frame.size.width/2, height: 50))
nextBtn.tag = 16
nextBtn.setTitle("Next", forState: .Normal)
nextBtn.backgroundColor = UIColor.darkGrayColor()
nextBtn.addTarget(self, action: "nextBtnAction", forControlEvents: UIControlEvents.TouchUpInside)
qView.addSubview(qLbl)
qView.addSubview(prevBtn)
qView.addSubview(nextBtn)
self.view.addSubview(qView)
}
And the buttons actions
func prevBtnAction() {
currIndex--
if currIndex >= 0 {
qLbl.text = qArray[currIndex]
} else {
currIndex++
}
}
func nextBtnAction() {
currIndex++
if currIndex < qArray.count {
qLbl.text = qArray[currIndex]
} else {
currIndex--
}
}
You can simplify even further but this is just for understanding. Hope it helps.
It depeds on your requirement, if each page contains single question and options no need of creating 10 different viecontrollers and you can use views efficiently,
If all questions are identical like just question and options you can use single reusable view. Unless you have each question will have different type of answers such as emojis, photo selection, etc.,
Better use reusable single view, move with animations and load appropriate data from objects. After completing all questions you can navigate to conclusion viewcontroller. My suggestion is not to create 10 unusable viewcontollers.
You can try following way,
Create one single view & array of your question then manage it using the previous/next button event in one controller. If you would like to display it with animation the need to some work on that.
You can even try to push the same controller every time with updated array index.

Set custom attribute on button

How can i set custom attributes on an button that is programmatically created?
I have this code to create an button:
var btn = UIButton(frame: CGRectMake(CGFloat(7 + add), 565, 39, 55))
btn.setTitle(String(Array(shuffledWord)[i...i]), forState: UIControlState.Normal)
btn.addTarget(self, action: "btnPressed:", forControlEvents: UIControlEvents.TouchUpInside)
btn.tag = 7 + add;
I can give some value to .tag but are there more thing like that? Can i create custom attributes to set on the button?
If you need custom properties on a class that doesn't belong to you, subclass it and use the subclass instead.
class MyCoolButton : UIButton {
var myCoolProperty : String = "coolness"
}
And later:
var btn = MyCoolButton( // ...
First I would start by looking at the class reference here
What attributes are you directly looking to change about the button? You can set it's title color, font, border color/width, corner-radius etc.
A lot of these can be mutated by accessing the button's layer as in
btn.layer.borderWidth = 2.0 and things of that nature.
EDIT: If you are looking to add custom features or store some properties then I would refer to Matt's answer about subclassing.

UISearchBar Scope Bar Position?

I am here on an iPad Application and i would like to know if its possible to move the Scope Bar from right to my UISearchBar to another position?
I would like to have my Scope Bar under my Search Bar. Is that possible?
Thanks in advance.
Ok this is my solution for that. Ill implemented my own segmented control to create a possibility for a search scope.
let categories = ["Scope1", "Scope2", "Scope3"]
segmentedControl.addTarget(self, action: "changeScope:", forControlEvents: .ValueChanged)
segmentedControl.frame = CGRectMake(8, 5, 800, 30)
segmentedControl.backgroundColor = UIColor.whiteColor()
segmentedControl.tintColor = UIColor.darkGrayColor()
// add it in a scrollView, because ill got too much categories here. Just if you need that:
scrollView.contentSize = CGSizeMake(segmentedControl.frame.size.width + 16, segmentedControl.frame.size.height-1)
scrollView.showsHorizontalScrollIndicator = false;
// set first category
segmentedControl.selectedSegmentIndex = 0
scrollView.addSubview(segmentedControl)
Here is the function for the scope bar, do wantever you want when a user switches a scope:
func changeScope(sender: UISegmentedControl) {
switch(sender.selectedSegmentIndex) {
}
}
In my case, ill got several resultArrays from an Webservice, and ill only show the selected result (normally ill Add them to 1 huge resultSet)
Hope that helps maybe someone else.

Appearing and Disappearing UIButton

I'm new to Objective C and swift (I guess we are all new to swift) but I am trying to make a UIButton appear and disappear in different locations on the screen in my app. This is what I've tried in one of my view controllers so far but it doesn't seem to work.
func addButton() {
var start: CFTimeInterval
var elapsedTime:CFTimeInterval
let Button = UIButton()
let picture = UIImage(named: "picture.png")
Button.setImage(picture, forState: UIControlState.Normal)
Button.frame = CGRectMake(0, 142, 106.6, 106.5)
self.view!.addSubview(Button)
while (elapsedTime < 1.0) {
elapsedTime = CACurrentMediaTime() - start
}
Button.removeFromSuperView()
}
You could use the convenient GCD API for the timing
dispatch_after(dispatch_time_t(1.0), dispatch_get_main_queue(), {
button.removeFromSuperView()
})
If it is always the same button, it would be better to create a variable or outlet and just recycle the button (you just let it appear and disappear by setting the alpha or the hidden property. If it is just supposed to be blinking, you could use a basic CAAnimations instead.
NB: Please get into the habit of using variable names that start with small letters, or you will end up mistaking them for class names.

Resources