How to reload data in UIViewController inside a UINavigationController - ios

I have 2 child ViewContollers in a NavigationController. There is a UISwitch in the 2nd one that changes how I query a backend API in the 1st one.
This is my code in the 2nd one:
#IBAction func ordersTapped(_ sender: UISwitch) {
if sender.isOn {
UserDefaults.standard.set(true, forKey: "showPastOrders")
} else {
UserDefaults.standard.set(false, forKey: "showPastOrders")
}
}
This is my code in the 1st one:
override func viewDidLoad() {
super.viewDidLoad()
let orderList = UserDefaults.standard.bool(forKey: "showPastOrders")
loadOrderData(past: orderList)
}
The problem is, whenever I swap back and forth between the ViewControllers, the Userdefaults boolean value I set, is not read. I tried overriding the viewDidAppear to obtain the value but that did not help either.
So how would I obtain the boolean value each time when I jump back and forth between the VCs?

maybe you can fix it by setting up a didSet call on the Bool.
var showPastOrders: Bool {
didSet {
orderList = UserDefaults.standard.bool(forKey: "showPastOrders")
// view.layoutIfNeeded()
}
}
Now if setting the showPastOrders is not set correctly I would recommend delegation or passing the data with the segue, depending on how you set up your project.
Hope it helps.

Related

Why are animated calls of UIView.isHidden = false compounded?

As you can see I'm having trouble formulating the question. Let me try to explain:
I'm using a search bar in my swift ios app. In order to get a desired animation effect I put it in a vertical stack view and then animate its isHidden property. This way search bar pushes down other children of the stack view as it animates in or pulls them up as it animates out. So far so good.
I've noticed a behavior that I think is strange. Could be a bug or could be me not understanding how things work. Basically if I call search bar hiding method x times in a row I need to call search bar showing method x times before it would show. I'd expect to have to make just one call to show search bar regardless of how many times I called hiding method. The issue doesn't exist other way around: if I call search bar showing code x times I only need to call hiding method once for it to go away. This doesn't happen if I set isHidden without animating it...
Here's a sample code and a video of the issue. I'd appreciate it if someone would help me understand this behavior.
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar! {
didSet {
searchBar.isHidden = true
}
}
#IBAction func showAction(_ sender: UIButton) {
expandSearch()
}
#IBAction func hideAction(_ sender: UIButton) {
collapseSearch()
}
private func expandSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = false
}
}
private func collapseSearch() {
UIView.animate(withDuration: 0.3){
self.searchBar.isHidden = true
}
searchBar.resignFirstResponder()
}
}
You should not call an asynchronous animation of searchbar x times, instead of I suggest you to keep its state in variable, something like isSearchBarHidden,
and check it before show/hide search bar. You could use just one method with such signature showSearchBar(show: Bool) and setting this variable there.
#IBAction func showAction(_ sender: UIButton) {
showSearchBar(true)
}
#IBAction func hideAction(_ sender: UIButton) {
showSearchBar(false)
}
private
func showSearchBar(_ show: Bool) {
guard isSearchBarHidden != show else {
return
}
UIView.animate(withDuration: 0.3, animations: {
self.searchBar.isHidden = show
}) {
self.isSearchBarHidden = show
if !show && searchBar.isFerstResponder {
searchBar.resignFirstResponder
}
}
}
private
var isSearchBarHidden: Bool = true
Also it is a good practice to check if your textView/textField/searchBar isFirstResponder before call resignFirstResponder.
Hope it will help. Good luck!

Many buttons to a single view controller

im developing an app that utilises many buttons( possibly 20 buttons) on one primary view controller that can are all able to activate a singular picker view within a pop up on a seperate view controller. i don’t think the answer is lots and lots segues. Is there a better approach I should be considering?
I’m thinking - some kind of multiuse segue that can be activated by any of the buttons, but nonidea how this is done.
Appreciate any advice
Mike
Set up all buttons to same action such as:
#IBAction func keyPressed(_ sender:UIButton){
// use button title string
self.keyString = sender.titleLabel?.text as! String
// or tag
self.keyTag= sender.tag?
self.performSegue(withIdentifier: "TheSegue", sender: self)
}
Then you would want to set up the View Controller that you are going to navigate to based on the state of the sender. So you would override the prepare:forSegue method as below.
override func prepare(for segue:UIStoryboardSegue, sender:Any?) {
let destController = segue.destination as! Dest_Controller_Class
// use tag or keyTitle to set controller attributes
// before view is shown
destController.keyTag = self.keyTag
destController.keyString = self.keyString
}
Now once you've navigated to the Dest_Controller_Class, you will have the properties of the button pressed locally in the view controller and could update the view as you see fit:
class Dest_Controller_Class: UIViewController {
var keyString: String?
var keyTag: Int?
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
if (keyString != nil) {
label.text = keyString;
// or likewise use tag
} else {
label.text = "keyString not set"
}
}
}

How to save on/off value of a switch with User defaults?

I have created a custom table view cell. In my app, there are two files. I have a separate file for the custom table view cell. Then I have a tableviewcontroller file for the table as a whole.
On my custom table view cell, there is a UISwitch. The initial value of this UISwitch is off. In the file for the custom table view cell, I have created a function for the switch. I have also created an outlet for the switch.
#IBOutlet var theSwitch: UISwitch!
#IBAction func mySwitch(_ sender: AnyObject) {
}
In my table view controller, I have already created the view did appear function. I am assuming what I need to do is save the value of the switch in
#IBAction func mySwitch(_ sender: AnyObject) {
}
Then in the table view controller, somehow call it in this view did appear function.
override func viewDidAppear(_ animated: Bool) {
}
What I am having trouble with is saving the value of the switch if it is changed. For example, if a user opens the app and turns the switch on, I want the app to save it, so that when the app is reopened, the value of the switch is still on. Hopefully, this can be done by using User Defaults. If someone can show me the code to do this, that would be great.
In your IBAction method you an save the value like this way
#IBAction func mySwitch(_ sender: UISwitch) {
UserDefaults.standard.set(sender.isOn, forKey: "Switch")
}
In your table view delegate method write this way to get the value from user default and set the status of you switch as per your previous value
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let yourCell = tableView.dequeueReusableCellWithIdentifier("ProfileCell", forIndexPath: indexPath) as! MenuProfileTableViewCell
yourCell.yourSwitch.setOn(UserDefaults.standard.bool(forKey: "Switch"), animated: true)
}
You can use setBoolForkey in swift 3 like this
UserDefaults.standard.set(true, forKey: "onoroff")
where you want to save and you can check the value in viewdidload() to change the status of switch according to the value of "onoroff"
theSwitch.setOn(UserDefaults.standard.bool(forKey: "onoroff"), animated: false)
As PiyushRathi said, you can do it with NSNumber, but you can do it via setting to Bool as well:
Swift 2.3
// saving switch value
NSUserDefaults.standardUserDefaults().setBool(theSwitch.on, forKey: "SwitchValue")
// getting back the value
theSwitch.setOn(NSUserDefaults.standardUserDefaults().boolForKey("SwitchValue"), animated: true)
For Swift 3 (As EmilioPelaez suggested):
// saving switch value
UserDefaults.standard.set(theSwitch.isOn, forKey: "SwitchValue")
// getting back the value
theSwitch.setOn(UserDefaults.standard.bool(forKey: "SwitchValue"), animated: true)
Also, if you have many UI components to save other than switch, you should also have a look at this UIViewController's "State Preserving Guide".
You can check value like this:
Swift 2
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.valueForKey("SwitchValue") != nil{
let object = userDefaults.valueForKey("SwitchValue") as? NSNumber
yourSwitch.on = (object?.boolValue)!
}
and for saving value to userDefaults:
userDefault.setValue(NSNumber(bool: yourSwitch.value), forKey:"SwitchValue")
Swift 3
let uDefault = UserDefaults.standard
if uDefault.value(forKey: "SwitchValue") != nil{
let object = uDefault .value(forKey: "SwitchValue") as? NSNumber
yourSwitch.isOn = (object?.boolValue)!
}
and set value
uDefault.set(NSNumber(bool: yourSwitch.value), forKey: "SwitchValue")
hope this helps

Can we make 1 UIButton for 2 action with swift?

i want to make 2 action for a button like that.
selected and deselected action for 1 button.
#IBAction func btntouch(sender: UIButton) {
if firsttouch
{
print bla bla
change button to selected style. maybe background color.
}
else
{
}
}
how can i do that?
In case you need to split two button statuses - like ON and OFF, try this:
var buttonSwitched : Bool = false
#IBAction func btntouch(sender: UIButton) {
//this line toggle your button status variable
//if true, it goes to false, and vice versa
self.buttonSwitched = !self.buttonSwitched
if self.buttonSwitched
{
//your UI styling
}
else
{
//your opposite UI styling
}
}
Create 2 IBActions:
#IBAction func touchDown(_ sender: AnyObject) {
print("down")
}
#IBAction func touchUp(_ sender: AnyObject) {
print("up")
}
When connecting the first one, make sure the event is set to touchDown. For the second one, make sure it is set to touchUpInside
Yes, you can. Depending on your requirements, you could store the current state of the button in the view controller or in the model.
If the visual change caused by the first touch needs to be persisted across opening and closing of your view controller, store the value indicating the change in the model; if you need to reset the visuals when the view controller shows, store the value in the view controller itself:
var firstTouch = true
#IBAction func btntouch(sender: UIButton) {
if firstTouch {
firstTouch = false
...
} else {
...
}
}

Error trying to transfer data from variable between storyboards

I have a game where I store the value of the high score of a player as "highScore", in the first view controllers.
What I want to do is to try to transfer this variable data, to the second view controller.
I entered in the following code in the first view controller's storyboard.
Btw, highscore2, is the variable that I delcared in the second view controller, to store the data from the first highscore variable.:
override func prepareForSegue(segue: UIStoryboardSegue!, sender:AnyObject!)
{
if (segue.identifier == "segueTest")
{
var svc = segue!.destinationViewController as! viewTwo;
svc.highScore2 = "High Score \(highScore)"
}
}
Now here is the code in my second view controller (viewTwo):
class viewTwo: UIViewController
{
#IBOutlet var highScoretwolbl: UILabel!
var highScore2:String!
override func viewDidLoad() {
highScoretwolbl.text = highScore2
}
}
For some reason, the code compiles, but the high score is not displayed, and is "nil".
Avoid forced unwrapping.
if let svc = segue.destinationViewController as? viewTwo{
svc.highScore2 = "High Score \(highScore)"
}
Then put in a breakpoint and make sure you got to all of these lines.
As stated in other answers, viewDidLoad only gets fired once. Since you
are using a forced unwrapped string, it is initially nil. It (highScore2) won't get a value until you set it which happens after viewDidLoad. Add some print statements so you can see the sequence of events.
Your best option is to use viewWillAppear. This will get fired every time
your view appears. This is what you want.
You don't need to use a force unwrapped variable. Just make a string a
make it empty initially.
override func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject?)
{
if segue.identifier == "segueTest"
{
if let svc = segue.destinationViewController as? viewTwo {
print("setting score in prepareForSegue")
svc.highScore2 = "High Score \(highScore)"
}
else {
print("something bad happened")
}
}
}
class viewTwo: UIViewController
{
#IBOutlet var highScoretwolbl: UILabel!
// The following uses forced upwrapping which means it could be nil
// if you never set it and your app will crash.
// var highScore2: String!
// Instead, do this:
var highScore2: String = ""
override func viewWillAppear(animated: Bool) {
// putting this here will ensure that every time the screen
// is shown, the label will get set from the string.
highScoretwolbl.text = highScore2
}
}
If I recall correctly, the viewDidLoad method is called only once when the view controller is first initialized. So you are setting the highScoretwolbl variable before highScore2 actually has any data, this is why it is nil.
I would recommend setting the text inside viewWillAppear so it updates every time the segue happens.
So something like
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
highScoretwolbl.text = highScore2
}
Here is a reference to the lifecycle of all the methods of a view controller. View controller lifecycle

Resources