When using the storyboard, you can draw 2 buttons onto it, and add then add them to an array by creating:
#IBOutlet private var cardButtons: [UIButton]!
This outlet collection is now an array of both buttons that loads before viewDidLoad as a variable.
How do I create this array of buttons programmatically without using storyboard?
I've tried this but it gives me a declaration error:
class ViewController: UIViewController {
var cardButtons = [CardView]()
let cardOne = CardView()
cardButtons.append(cardOne)
...rest of viewController standard class
}
There a several points of time in the ViewController's lifecycle where you might want to fill the cardButtons array. For example you could do this in viewDidLoad.
class ViewController: UIViewController {
var cardButtons = [CardView]()
override func viewDidLoad() {
super.viewDidLoad()
let cardOne = CardView()
cardButtons.append(cardOne)
}
}
Keep in mind that viewDidLoad is called every time the View is loaded into memory. In my example cardOne would be recreated every time. To avoid this you could store cardOne in a instance var, as you did initially.
class ViewController: UIViewController {
var cardButtons = [CardView]()
let cardOne = CardView()
override func viewDidLoad() {
super.viewDidLoad()
cardButtons.append(cardOne)
}
}
As I said, there several points of time in the ViewController's lifecycle where you might want to fill the cardButtons array. Other functions could be:
viewDidAppear(), to fill the area every time the view appears.
init(), if you are not using storyboard at all.
Here is what I ended up doing:
class ViewController: UIViewController {
let uiView: UI = {
var ui:UI = UI()
let cardOne = CardView()
let cardTwo = CardView()
ui.addButton(item: cardOne)
ui.addButton(item: cardTwo)
return ui
}()
...rest of viewController standard class
}
class UI {
var cardButtons = [CardView]()
func addButton(button: CardView){
cardButtons.append(button)
}
}
Related
in the past 2 weeks I tried to solve this problem in Swift which I had.
I am quite new to Swift and developing IOS APP's. I want to change a property of an Label from a other Class. I know from other languages like Java that you can get the instance of an Class and change the Label from outside. But I don't know how to do this in Swift because I don't know where and how the ViewController is created as an Object.
I have a lot of sourcecode. That's why I created a simple Game and ViewController Class to show you my problem.
class ViewController: UIViewController {
#IBOutlet weak var Points1: UILabel!
var game = Game()
override func viewDidLoad() {
super.viewDidLoad()
}
}
class Game {
func IncrementPoints() {
//Here I want to Increment the Points by getting the Label from the ViewController
}
}
In my code it's very important that I change the Label or have access to the label from outside.
In this easy Example it would be possible to execute this function in the ViewController but that is not possible.
I would be very happy if anyone could help me :)
This is not a good approach and there are better ways to do it. But if you want to access label in game you need to pass current viewcontroller as argument in a function
class Game {
weak var parentVC: ViewController?
var points = 0
func setVC(vc:ViewController){
self.parentVC = vc
}
func incrementPoints(by value: Int) {
points += value
parentVC?.points1 = "\(points)"
}
}
And in viewController pass self as argument
class ViewController: UIViewController {
#IBOutlet weak var Points1: UILabel!
var game = Game()
override func viewDidLoad() {
super.viewDidLoad()
game.setVC(vc:self)
}
}
In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.
My question is, how can I make it so redDot and wCircle can be accessed from the Second viewController so they can become hidden or not hidden. They are not connected directly, but you can get to them with different viewControllers.
First viewController
class SecondViewController: UIViewController
{
#IBOutlet weak var redDot: UIImageView!
#IBOutlet weak var wCircle: UIImageView!
}
Second viewController
class ProgressViewController: UIViewController {
#IBOutlet weak var rDot: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
rDot.isUserInteractionEnabled = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(rdotimageTapped(tapGestureRecognizer:)))
rDot.addGestureRecognizer(tapGestureRecognizer)
view.bringSubview(toFront: rDot)
}
func rdotimageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
wcircle.isHidden = true
redDot.isHidden = false
view.layoutIfNeeded()
print("It worked")
}
Im not sure if this is a right way but what I would do is have a reference of UIImageView in my second VC and then set that to the imageView of my first VC.
So add these two in your "ProgressViewController"
var redDot: UIImageView?
var wCircle: UIImageView?
and then in your prepare for segue pass your current UIImageView.
let progressViewController = segue.destination as! ProgressViewController
progressViewController.redDot = self.redDot
progressViewController.redDot.wCircle = self.wCircle
You can achieve that by adding a new member to your second and third view controllers, this member is a closure:
var updateFirstViewControllerImageViews: (() -> Void)?
Then override prepare for segue method of your firstViewController, and set updateFirstViewControllerImageViews for each of your second and third view controller like this:
secondViewController.updateFirstViewControllerImageViews = {
// Update your image views here in the way you want!
}
Now, in your second and third view controllers you can use that closure wherever you want like this:
self.updateFirstViewControllerImageViews?()
I have 2 controllers
and have got 1 global variable, the problem is if I go to controller 2 and click on button northAmericaClick, it will navigate back to control 1, but the value of global variable won't change!
this is my code
controller 1
class OurViewController: UIViewController {
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var selectedServer: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
selectedServer.setTitle(selected server, forState: UIControlState.Normal) // selected server this is global variable
}
controller 2
class selectServerController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func northAmericaClick(sender: AnyObject) {
selectedserver = "North America"
self.navigationController?.popViewControllerAnimated(true)
}
From
You need to use a delegate. Here is an example how do use a delegate in Swift.
On your first ViewController, set your delegate when you load the second VC:
For example, if you are using the Storyboard Editor:
var secondViewController = (segue.destinationViewController.visibleViewController as MySecondViewControllerClass)
secondViewController.delegate = self
Write a Protocol and define a func to write you values back
For example, create a file called "Protocol.swift" and write something like that:
protocol writeValueBackDelegate {
func writeValueBack(value: String)
}
Add the function to your FirstViewController
func writeValueBack(value: String) {
// this is my value from my second View Controller
}
And to your ViewControllerClass
class ViewController: UIViewController, writeValueBackDelegate
Go to the Second View Controller, and add the delegate here:
class SecondViewController: ViewController {
// delegate for FirstViewController
var delegate: writeValueBackDelegate?
On your Second View Controller, you can now use this to call the func in the first View Controller an pass data.
delegate?.writeValueBack("That is a value")
You also need to indicate that your first view controller implements the protocol: class ViewController: UIViewController, writeValueBackDelegate {
A part of doing it with delegate you also can create singleton class ViewControllersDataModel class and share the variable using it:
import Foundation
class ViewControllersDataModel {
static let sharedInstance = ViewControllersDataModel()
var selectedserver: String = ""
private init() {
}
}
And call it like this:
ViewControllersDataModel.sharedInstance.selectedserver = "Selected Option";
Ok, I can do this with this code, only check when viewWillDisapear and call the parent of this view controller in the navicationController:
override func viewWillDisappear(animated: Bool) {
if ((self.navigationController!.viewControllers.last?.isKindOfClass(ActivityMyViewController)) == true){
let backView:MyViewController = self.navigationController!.viewControllers.last as! MyDetailViewController
backView // do whatever you want
}
}
I hope this code can help you, good luck
thanks guys for helping ;)
it was very simple
i just use then when it comeback ^^"
override func viewWillAppear(animated: Bool) {
selectedServer.setTitle(selectedserv, forState: UIControlState.Normal)
}
Is it possible to have an #IB Action function inside of viewDidLoad() ?
The action is a simple one - a Stepper that increases other label.text values accordingly. However, the values that the stepper needs to work with depend on the return content of a url - which are only known after the viewDidLoad() of course.
So I think I can't have the IBaction way up on top before the viewDidLoad(), and the error I get if I try to do my IB action inside of the viewDidLoad() is:
"Only instance methods can be declared ‘IBAction' ”
EDIT
Let me clarify myself, sorry for the confusion. I know I need an outlet to get the UIStepper values from. I have that:
#IBOutlet weak var stepper: UIStepper!
I then have an action also connected to same UIStepper that will increase/decrease value of a label's text (new_total) accordingly:
#IBOutlet weak var new_total: UILabel!
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value))"
}
However, I want to start out with a value (todays_price) I'm getting back from a json request and use that as a starting point, to multiply it using the stepper and put the multiplied value into the label's text.
I have a struct in a separate file that defines my object so:
struct PassengerFromOtherBus {
var fname: String?
var lname: String?
var todays_price: Int?
init(json: NSDictionary) {
self.fname = json["fname"] as? String
self.lname = json["lname"] as? String
self.todays_price = json["todays_price"] as? Int
}
}
So later on in the view controller, inside of the viewDidLoad(), after connecting to the URL and then parsing it using NSJSONSerialization and a bunch of other code here (that I don't need to confuse you with) I finally have my value todays_price. So my question is, how do I get my action to use that value when it's only known inside of my viewDidLoad()? Xcode will not even let me connect the IBAction to anywhere inside the viewDidLoad function!
This is not done with an Action but with an Outlet. Connect the Stepper from IB as an Outlet to your ViewController. Then just set the values of the Stepper in ViewDidLoad.
I would never go directly from a UIStepper.value to UILabel.text.
Use an intermediary variable to store the value.
Do the same for the return from the JSON. By setting a didSet function on those variables you can update the UI when any of the values is updated.
class FirstViewController: UIViewController {
var todays_price: Int = 0 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
var stepperValue : Int = 1 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
#IBOutlet weak var myStepper: UIStepper!
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
//
let returnValueFromJson = 10
todays_price = returnValueFromJson
}
#IBAction func stepperUpdate(sender: AnyObject) {
stepperValue = Int(myStepper.value)
}
}
Just add a variable to the top of your view controller to hold the value from your json request. Then in viewDidLoad you update that variable, and then you can use it to set your label and inside the IBAction (that doesn't have to be inside viewDidLoad).
So you would do something like this:
class WhateverViewController: UIViewController {
var todays_price: Int!
override func viewDidLoad() {
super.viewDidLoad()
todays_price = // The value you got from json goes here
new_total.text = "\(todays_price)"
}
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value) * todays_price)"
}
}