I am working on a project, with a UITabBarcontroller, in Xcode using swift code.
In this project the users can (among other stuff) choose their favorite images. These favorite images will be set on 30 buttons I have on my favorites UIViewController.
To accomplish what I want, I developed some very basic code: I assigned 30 IBOutlets for the 30 buttons and I made 30 (big) if-statements. It in fact works, but I know this code can be done in a simpler and more concise manner. And I am not yet able to do that.
I really want to learn, so can anybody help me with a way to rewrite this code? Help is much appreciated :). Just a push in the right direction would already be great
Should I for example assign tag values to the 30 buttons, and ‘find’ the appropriate buttons with .viewWithTag (instead of the 30 IBOutlets). And should I for example use some sort of loop for dealing with the different counts of the array? (see below)
Here is my code:
// I have created a subclass of UITabBarController
class TabBarData: UITabBarController {
/* In this class I have initialized an empty array to share between the various tabs.
The array will be populated with the favorites chosen by the user. */
var array = [Int]()
}
/* There are multiple ViewControllers in my project that have the same
code for adding a favorite. So for now I describe one example ViewController */
class exampleVC: UIViewController {
/* A variable that identifies the image by the number. There a a few
hundred images in the project, every images has its own identifying number */
var imageNumber = 0
// This function will execute if user adds a favorite:
func userAddedFavorite(imageNumber: Int) {
// The ViewController within the TabBarController getting acces to the properties
if let tbc = self.tabBarController as? TabBarData {
// The Array can not be bigger then a count of 30:
if tbc.array.count < 30 {
// When a user adds a new favorite image, the array gets filled with a new value:
tbc.array.append(imageNumber)
// Now I set the button images, in viewWillAppear, for my favorites VC:
class Favorites: UIViewController {
// IBOutlets for the 30 buttons
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
// etcetera…
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let tbc = self.tabBarController as? TabBarData {
if tbc.array.isEmpty {
print("The user has no Favorites at the moment. The array is empty")
} else if tbc.array.count == 1 {
// At the moment the images in my project have been named: test1/test2/test3 etc...
button1.setImage(UIImage(named: "test\(tbc.array[0])"), forState: .Normal)
} else if tbc.array.count == 2 {
button1.setImage(UIImage(named: "test\(tbc.array[0])"), forState: .Normal)
button2.setImage(UIImage(named: "test\(tbc.array[1])"), forState: .Normal)
} else if tbc.array.count == 3 {
button1.setImage(UIImage(named: "test\(tbc.array[0])"), forState: .Normal)
button2.setImage(UIImage(named: "test\(tbc.array[1])"), forState: .Normal)
button3.setImage(UIImage(named: "test\(tbc.array[2])"), forState: .Normal)
} else {
print("etcetera.....,the if-statements getting bigger each count up......")
}
Instead of making 30 IBOutlets (one for each button,) make a single IBOutletCollection that holds all thirty:
#IBOutlet var buttons: [UIButton]!
Then you can:
for (index, item) in tbc.array.enumerate() {
buttons[index].setImage(UIImage(named: "test\(item)"), forState: .Normal)
}
Related
I'm creating a slider programmatically due to and Xcode bug, (don't let me center the thumb when I change the slider values, so I decided to do it using code) and I want to have a variable which saves the slider value. Is there a way to associate the variable and the target object with the control, similar to "addTarget", but instead of an action, its a variable?
I don't know if I explained myself but tell me if I need to be more specific. Thanks in advance :) for helping me.
This is my code:
import UIKit
class ViewController: UIViewController {
var slider: UISlider!
#IBOutlet weak var sliderVar: UISlider!
var currentSliderValue = 0
override func viewDidLoad() {
super.viewDidLoad()
slider = UISlider(frame: CGRect(x: 98, y: 173, width: 699, height: 30))
slider.center = self.view.center
slider.minimumValue = 1
slider.maximumValue = 100
slider.value = 50
slider.isContinuous = true
slider.addTarget(self, action: #selector(sliderMoved(_:)), for: UIControl.Event.valueChanged)
self.view.addSubview(slider)
}
#IBAction func sliderMoved(_ sender: UISlider) {
currentSliderValue = lroundf(sender.value)
}
}
My function “sliderMoved” changes the sliderCurrentValue variable, but this var won’t change until I use the slider and move it. I also have a button there, that when you touch it up it shows the slider value, but the “sliderCurrentValue” only changes its value when the slider is moved. I was thinking of creating an IBOutlet but I don’t know how to connect this one with the slider.
As far as I understand you need to bind variable to slider's value. There are several ways to achieve it. There is one way. Declare variable with custom setters and getters
class ViewController: UIViewController {
var slider: UISlider!
var sliderValue: Float {
set {
// use optional chaining here helps avoid crashes
slider?.setValue(newValue, animated: true)
}
get {
// if slider control hasn't created yet you need to return some dummy value
slider?.value ?? -1
}
}
func viewDidLoad() {
// your current implementation here
}
}
I have two UIButtons that I want to use to set an A/B value to a variable before I save data to a database. I want a button to become selected when tapped, and deselected when the other button is tapped, and vice versa. What is a good solution for accomplishing this programmatically or in Interface Builder?
In order to set an "A/B value" as you mention, the easiest option would be to use a UISwitch or -in the general case of possibly more than 2 options- a UISegmentedControl (as #rmaddy suggested in the question's comments) .
These controls have built-in the "choose just one out of many" functionality that you are looking for.
The drawbacks of the switch are:
It has to be either on or off (does not support a selection state of "neither A nor B")
You can't have separate title labels for each state.
If you still want two separate UIButton instances, you can:
Have references to both buttons in your view controller (#IBOutlets wired using Interface Builder), e.g.:
#IBOutlet weak var leftButton: UIButton!
#IBOutlet weak var rightButton: UIButton!
Implement the action method for both buttons in such a way that it sets the selected state of the tapped button, and resets the other one. For example:
#IBAction func buttonAction(sender: UIButton) {
if sender == leftButton {
leftButton.isSelected = true
rightButton.isSelected = false
} else if sender == rightButton{
leftButton.isSelected = false
rightButton.isSelected = true
}
}
This is a quick-and-dirty solution for just two buttons. If you want a generic radio group of n-buttons, there are open source solutions on GitHub, etc...
Try this.
First create both button separate #IBOutlet.
#IBOutlet weak var btnYes: UIButton!
#IBOutlet weak var btnNo: UIButton!
Set Both Button Tag Like this and you also set tag using storyboard.
override func viewDidLoad() {
super.viewDidLoad()
btnYes.tag = 1
btnNo.tag = 2
}
Implement Common #IBAction method for both buttons
#IBAction func btnYesNoTapped(_ sender: UIButton) {
if sender.tag == 1 {
self.IsBtnSelected(isSelect: true, with: self.btnYes)
}else {
self.IsBtnSelected(isSelect: true, with: self.btnNo)
}
}
Create Custome Method
func IsBtnSelected(isSelect:Bool,with sender:UIButton){
self.btnYes.isSelected = false
self.btnNo.isSelected = false
sender.isSelected = isSelect
}
you can use following function for creating a radio button behaviour, you have to btn outlet to be selected and array of both outlets to this function. instead ofcolor you can also compare images and set images. for getting a required value yo can create a variable in viewcontroller and assign this variable a value in IBAction of btn and you can call this function from IBAction.
func radioButton(_ btnToBeSelected: UIButton, _ btnArray: [UIButton]) {
for btn in btnArray {
if btn == btnToBeSelected {
btn.backgroundColor = UIColor.black
//selected btn
//You can also set btn images by
//btn.setImage(<#T##image: UIImage?##UIImage?#>, for: <#T##UIControlState#>)
} else {
btn.backgroundColor = UIColor.red
//not selected btn
}
}
}
In iOS , you would have to do it manually.See the below approaches,
Use a switch . Using a UISwitch would be better if the option indicates a on/off state.
Use a same method when the button is pressed. Whenever the method gets called deselect the other button/buttons and select the pressed button. You can use tags or keep a reference of the buttons to differentiate between them.
Lastly , keep different methods for each buttons . Just deselect the other buttons whenever the button is pressed.
You can follow the above approaches by using interface builder or programmatically.
You can achieve it like below
I have implemented it for dates which are in TableView you just need to do little modifications
enum filterDateSelectableOptions:Int {
case AssignDate
case DueDate
case CompletionDate
}
//Assign Date selected by default
var currentSelectedFilterDate:filterDateSelectableOptions = .AssignDate
Now
func btnRadioButtonTapped(sender:UIButton) {
switch sender.tag {
case kTableViewRow.AssignDate.rawValue:
self.currentSelectedFilterDate = .AssignDate
case kTableViewRow.DueDate.rawValue:
self.currentSelectedFilterDate = .DueDate
case kTableViewRow.CompletionDate.rawValue :
self.currentSelectedFilterDate = .CompletionDate
default:
break;
}
//sender.isSelected = !sender.isSelected
self.tblFilterList.reloadData()
}
in cellForRow I have
// THIS IS DIFFERENT ENUM SO +1 is required in my case
case .AssignDate,.DueDate,.CompletionDate :
let button = buttonRadioCircle
button.tag = row.rawValue
cell.accessoryView = button
button.addTarget(self, action: #selector(btnRadioButtonTapped(sender:)), for: .touchUpInside)
button.isSelected = self.currentSelectedFilterDate.rawValue + 1 == row.rawValue
}
Recently, I have started to study Swift with story board, Xcode, etc.
I am struggling to understand the principals of ViewController code, as it seems to miss a lot of essentials - presumably to try and make things more simple - but it just isn't for programmers who have come from else where.
import UIKit
class ViewController: UIViewController {
//Properties
#IBOutlet weak var textFieldName: UITextField!
#IBOutlet weak var submit: UIButton!
#IBOutlet weak var submit1: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//Actions
#IBAction func onSubmit(sender: UIButton) {
switch(sender){
case submit:
print("only 1");
case submit1:
print("222");
default:
print("nothing");
}
}
}
After following a basic tutorial, I have tried playing around on my own to test out my understanding. The IBOutlets, which define elements of the storyboard requires me to actually drag and drop it into the code in order for it to correctly correspond to the element. When I just type
#IBOutlet weak var submit1: UIButton!
Without having dragged it from storyboard - it does not work / correspond. So essentially there is more backend that I do not have control over?
Also, Actions. How come it prints without the method being called?! I must be missing something obvious - something to do with the attributes?
I want to be able to code things myself, and identify elements/buttons/etc myself without dragging and dropping, and not seeing the associations - submit1 and submit, at the moment, are set to the exact same thing: UIButton!
I am aware and accept my confusion and interpretation is clearly flawed and wrong. But could someone shed some light on the lower level activities of ViewController?
Thanks.
If you don't want to drag and drop you can write, i don't see where is the problem
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
button.backgroundColor = .greenColor()
button.setTitle("Test Button", forState: .Normal)
button.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
self.view.addSubview(button)
I'm new to iOS development and I'm working on a Fire Safety App. I would like to give the user the option of 3 risk levels: Low, Medium and High by using (I suppose) 3 UIButtons which the user can tap, and which ever one they tap would turn a certain colour.
I've managed to get 3 UIButtons working, and they can change colour when tapped but if I tap another I can't get the previous button to change back and I've had difficulties getting a value for them (For example when submitted if the user pressed Low I would use the number 2 for calculations)
To show this I did a quick drawing:
Button Example
Thanks for your help :)
==== EDIT ====
I am now using a Segmented Control instead of UIButtons to do this. But I need to know how to use them in an if statement
#IBOutlet var RiskChoice: UISegmentedControl!
#IBOutlet var ValueLabel: UILabel!
#IBOutlet var CalcButton: UIButton!
#IBAction func Calculate(sender: UIButton) {
if (RiskChoice.selectedSegmentIndex == 0){
ValueLabel.text = String("Low")
}
Nothing appears in the Label I have set up.. Could someone direct me here?
You can just reset all the 3 buttons to their default state when any of the button is tapped and then highlight the current button.
#IBAction func buttonClicked(sender: UIButton) {
/** Reset all button to default state*/
resetAllButtons()
/** Highlight current button */
sender.backgroundColor = UIColor.redColor()
sender.titleColor = UIColor.whiteColor()
}
private func resetAllButtons() {
button1.backgroundColor = UIColor.clearColor()
button1.titleColor = UIColor.blackColor() /// Or use this : button1.setTitleColor(UIColor.blackColor, forState: .Normal)
button2.backgroundColor = UIColor.clearColor()
button2.titleColor = UIColor.blackColor()
button3.backgroundColor = UIColor.clearColor()
button3.titleColor = UIColor.blackColor()
}
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.