How to move function with button sender to a separate file? - ios

I have a difficulty in a simple task. I googled this topic, but other examples are complicated by additional syntax that I don't understand yet. Can you help me to solve it or give link if there is already was similar topic.
I need to move the function responsible for selecting the button to a separate file, because if the number of buttons increases, it will turn into a large sheet. So made a function in separate swiftfile, but naturally the new file does not know about any buttons in viewController and can't find it in scope. Also If i’m not mistaken i need give Bool and return String.
How can I transfer a function with button sender to a separate file so that it returns non-optional text value back in the ViewController?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var Label1: UILabel!
#IBOutlet weak var BTC: UIButton!
#IBOutlet weak var ETC: UIButton!
#IBOutlet weak var LTC: UIButton!
var choice = "Choose coin"
var coinType = getCoinType()
#IBAction func optionSelected(_ sender: UIButton) {
BTC.isSelected = false
ETC.isSelected = false
LTC.isSelected = false
sender.isSelected = true
if BTC.isSelected == true{
ETC.isSelected = false
LTC.isSelected = false
choice = "BTC"
Label1.text = choice
}else if ETC.isSelected == true{
BTC.isSelected = false
LTC.isSelected = false
choice = "ETC"
Label1.text = choice
}else if LTC.isSelected == true{
ETC.isSelected = false
LTC.isSelected = false
choice = "LTC"
Label1.text = choice
}
}
}
new file, i can't understand how to get sender from buttons here
import Foundation
func getCoinType() -> String{
var choice: String
// my if else function
return choice
}
P.S. In general, is it possible to make it easier, without using UIPickerView?

On how to add functions in different swift file you have a few options. This a simple one: Create a new swift file and name it ViewController+Extenstions.swift (you can use any name though). Then add an extension to your ViewController class and add your function like this:
extension ViewController {
func getCoinType() -> String {
var choice: String
// my if else function
return choice
}
}
You can add as many functions as you need and in different files (all extensions to ViewController but of course with different file names).
PS, in your optionSelected function, you are setting:
BTC.isSelected = false
ETC.isSelected = false
LTC.isSelected = false
I don't know what you're trying to achieve, but by doing so, those if-else will never be executed since your are setting the three buttons as not-selected!
UPDATE:
For your button selection problem, you can do this:
1- Add tags to your buttons
You can do it inside viewDidLoad method. This way you can differentiate between different buttons.
override func viewDidLoad() {
super.viewDidLoad()
BTC.tag = 0
ETC.tag = 1
LTC.tag = 2
}
2- Connect the action of buttons
Although your three buttons can have three different handler functions, it's easier to connect all of them to a single handler. However, we can know which button is tapped based on the value of the tag we assign in the previous step. So, connect all buttons to optionSelected(_ sender: UIButton) through Storyboard (because you have used Storyboard.
3- Rewrite getCoinType function
func getCoinType(tag: Int) -> String? {
var choice: String?
switch tag {
case 0:
choice = "BTC"
case 1:
choice = "ETC"
case 2:
choice = "LTC"
default:
choice = nil
}
return choice
}
Buttons' handler
Now when a button is tapped we call getCoinType function with that button's tag as input argument. It will return the string and we assign it to the Label1:
#IBAction func optionSelected(_ sender: UIButton) {
let choice = getCoinType(tag: sender.tag)
Label1.text = choice
}
And you're done!

Related

Swift: one button to alternate multiple test message in UIView

I am trying to implement one button- one click to display the first message, the second click would change to second message, the third click would change to the third message.
I looked up to one possible solution is to use UITapGestureRecognizer - single tap and double tap, which means I can trigger the button (single tap to display the first message and double tap to display the second message).
However, if I have more than two lines and I just want to display them by clicking each one (like animation). Would that be possible to just deal with that inside one UIView and UIbutton?
I currently have 3 simple messages:
#IBOutlet weak var Textfield: UITextView!
#IBOutlet weak var Changingbutton: UIButton!
#IBAction func ChangingTapped(_ btn: UIButton) {
Textfield.text = "Changing to driving"
Textfield.text = "Changing to walking"
Textfield.text = "Changing to cycling"
}
The problem now is when I click the button it would just go the last message. That might not be a clever way to do so.
Thanks so much for the inputs and I am sorry if that is a rather simple question.
You can implement a custom CaseIterable enumeration to perform a loop so that you can get the next element every time you press a button:
extension CaseIterable where Self: Equatable {
var allCases: AllCases { Self.allCases }
var nextCase: Self {
let index = allCases.index(after: allCases.firstIndex(of: self)!)
guard index != allCases.endIndex else { return allCases.first! }
return allCases[index]
}
#discardableResult
mutating func next() -> Self {
self = nextCase
return self
}
}
Create a enumeration with your transportation modes:
enum Mode: String, CaseIterable {
case cycling, driving, walking
}
add a mode property to your view controller and set the initial value
var mode: Mode = .cycling
Now you can simply call the next mode method every time you press the button:
func ChangingTapped(_ btn: UIButton) {
Textfield.text = "Changing to " + mode.next().rawValue
}
Note: It is Swift naming convention to name your methods and properties starting with a lowercase letter.
Why not just set a counter and increment it every time your IBAction is activated?
var x = 0
#IBAction func ChangingTapped(_ btn: UIButton) {
if(x==0){
Textfield.text = "Changing to driving"
}
else if(x==1){
Textfield.text = "Changing to walking"
}
else{
Textfield.text = "Changing to cycling"
}
x +=1
//if x needs to be reset
/*
if(x > 2) x = 0
*/
}

Setting isHidden to false on x amount of elements

I am trying to unhide n number of elements depending on the users input into a text field.
So the user enters a number between 1 - 5 in the text field then clicks submit which calls createSplit. As you can see, it unhides a view and then I want it to loop x (x being the number the user inputs) amount of times to unhide day(i)View textfield
#IBAction func createSplit(_ sender: Any)
{
noOfExerciseView.isHidden = false
let noOfDays: Int = Int(numberOfDays.text!)!
for i in 1...noOfDays
{
day\(i)View.isHidden = false
}
}
I have a working solution but it's not the most efficient so I hope someone can help doing this an efficient way.
#IBAction func createSplit(_ sender: Any)
{
noOfExerciseView.isHidden = false
let noOfDays: Int = Int(numberOfDays.text!)!
for i in 1...noOfDays
{
if (i==1)
{
day1View.isHidden = false
} else if (i==2)
{
day2View.isHidden = false
} else if (i==3)
{
day3View.isHidden = false
} else if (i==4)
{
day4View.isHidden = false
} else if (i==5)
{
day5View.isHidden = false
}
}
}
String interpolation cannot be used to set the name of a variable:
day\(i)View.isHidden // does not work
Your best bet is to use an outlet collection to define all your day views.
Instead of this:
#IBOutlet var day1View: UITextField!
#IBOutlet var day2View: UITextField!
#IBOutlet var day3View: UITextField!
//...
Do this:
#IBOutlet var dayViews: [UITextField]!
Then you can write your loop like this:
for i in 0...noOfDays-1
{
dayViews[i].isHidden = false
}
Note that to do this, you'll need to delete the existing outlets and reconnect them.
If you're using a storyboard, then when you Control-drag from your first text field to your class file, select Outlet Collection for the Connection type and name it dayViews. To add the remaining text fields to the collection, just Control-drag from each one to the dayViews var in your class file.

Adding user inputs from textfield into an array

I am trying to add users names that they have put in a textfield to a basic array. I don’t even know where to start!
The functionality basically is this:
User adds a name in text field
Name is stored in an array so it can be referenced again somewhere else.
As you can tell I’m new to Xcode. I have been taking classes all of which have failed to explain this process. Any help is good Help ! Please :):)
// my view controller so far
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var name1: UITextField!
#IBOutlet weak var name2: UITextField!
#IBOutlet weak var name3: UITextField!
var namesArray : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
name1.delegate = self
name2.delegate = self
name3.delegate = self
self.namesArray.append(self.name1.text!)
self.namesArray.append(self.name2.text!)
self.namesArray.append(self.name3.text!)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
if textField.returnKeyType == UIReturnKeyType.done {
self.namesArray.append(self.name1.text!)
print(namesArray)
}
return true
}
I am going to assume you have all your logic mashed into one view controller since you are still learning and not worried about larger architecture decisions yet.
At the top of your class, you need an array variable:
// Creates an array and initializes it to be empty.
var userNames: [String] = []
I suspect you also have a button, so that when you press on the button, it grabs the text from the textfield and stores it in the array. You need to set up an IBAction method for that button, and in that method...
// Grab the text, save it in the array, and clear the text.
self.userNames.append(self.myTextField.text)
self.myTextField.text = ""
Later on, you can easily see the list of userNames by accessing self.userNames. e.g.:
let firstUser = self.userNames.first
let lastUser = self.userNames.last
let user47 = self.userNames[46] // assuming you have that many users

How do I make two UIButtons perform like radio buttons in Swift?

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
}

how to hide/show a button in swift

I'm trying to have an if statement that will make a button hidden when a label displays a certain status, and appears when the label says something else. The name of the label is Status, and when it shows "Closed", I want it hidden, and when it shows "Open", it will appear.
var query3 = PFQuery(className:"Status_of_game")
query3.findObjectsInBackgroundWithBlock{
(namelist3: [AnyObject]!, error : NSError!) -> Void in
for list3 in namelist3 {
var output = list3["StatusType"] as String
self.Status.text = output
println(output)
if self.Status.text == "Closed"
{
Purchase().enable = false
}
}
}
As #LAmasse says, you want to use button.hidden = true. button.hidden was renamed to button.isHidden in Swift 3
The code you posted doesn't make sense.
if self.Status.text == "Closed"
{
Purchase().enable = false
}
What is Purchase? From the capitalized name, it seems to be a class. If so, the expression Purchase() is likely creating a new instance of the Purchase class, which makes no sense. Why are you making a function call? If that is creating a new Purchase object then that code is pointless. (You would create a new object inside the if statement that would be discarded on the very next line since you don't keep a strong reference to it.)
You want to set up an IBOutlet for your button and connect it in Interface Builder.
The declaration might look like this:
Class MyViewController: UIViewController
{
#IBOutlet weak var theButton: UIButton!
//The rest of your view controller's code goes here
}
If the outlet is connected to your button, there should be a filled-in circle to the left of the line of code. It looks like this:
And then your code to show/hide the button might look like this:
func showQueryResults
{
var query3 = PFQuery(className:"Status_of_game")
query3.findObjectsInBackgroundWithBlock()
{
(namelist3: [AnyObject]!, error : NSError!) -> Void in
for list3 in namelist3
{
var output = list3["StatusType"] as String
self.Status.text = output
println(output)
if output == "Closed"
{
theButton.isHidden = false //changed to isHidden for Swift 3
}
}
}
}
It isn't clear to me why you'd loop though all of the results from your query and and show the button if the "StatusType" of any of the results is == "Closed".
Finally, I'm not very familiar with parse. If the completion block for the findObjectsInBackgroundWithBlock method doesn't get called on the main thread you will have to change that code to make the UI updates on the main thread.
EDIT:
I've since learned that Parse executes its completion handlers on the main thread, so you don't need to worry about UI calls from Parse completion handlers.
SWIFT 3
I created an
IBOutlet: loadingBDLogo
To Show:
loadingBDLogo.isHidden = false
To Hide:
self.loadingBDLogo.isHidden = true
The sample code for hiding a button in Swift:
import UIKit
class ViewController: UIViewController {
// Create outlet for both the button
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//Set button2 hidden at start
button2.isHidden = true
}
//Here is the action when you press button1 which is visible
#IBAction func button1(sender: AnyObject) {
//Make button2 Visible
button2.isHidden = false
}
}
And
You have to make the UIButton a property of the class if you want to keep a reference to it. Then you can access it using self.takePhotoButton.
To hide a button, use button.hidden = true
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/occ/cl/UIView

Resources