I'm trying to complete my favorite button by saving it state even when i quit the view \ App . it will be great if anyone could show me how can i do this, I'm using Xcode 8 and coding with Swift 3.
Current Button Code :
//create a new button
let Favoritebutton: UIButton = UIButton(type: UIButtonType.custom)
//set image for button
Favoritebutton.setImage(UIImage(named: "EmptyHeart.png"), for: .normal)
Favoritebutton.setImage(UIImage(named: "FilledHeart.png"), for: .selected)
//add function for button
Favoritebutton.addTarget(self, action: #selector(self.button), for: .touchUpInside)
//set frame
Favoritebutton.frame = CGRect(x:0,y: 0,width: 35,height: 35)
let barButton = UIBarButtonItem(customView: Favoritebutton)
//assign button to navigationbar
self.navigationItem.rightBarButtonItem = barButton
let state = UserDefaults.standard.bool(forKey: "isSaved") ?? false
}
#IBAction func button(sender: UIButton) {
sender.isSelected = !sender.isSelected
UserDefaults.standard.set(true,forKey: "isSaved")
UserDefaults.standard.synchronize()
if let Favoritebutton = sender as? UIButton {
if Favoritebutton.isSelected {
// set selected
Favoritebutton.isSelected = true
UserDefaults.standard.set(true, forKey: "MY_FAV_KEY")
UserDefaults.standard.synchronize()
// set badge to tabbar item.
let tabItem = self.tabBarController?.tabBar.items![3]
sel_val = tabItem?.badgeValue
if(sel_val == nil){
sel_val = "0"
}
let sel_num = Int(sel_val!)
tabItem!.badgeValue = String(format: "%d", sel_num! + 1) as String
let Fav: NSMutableArray = []
Fav.add(barImage)
Fav.add(barName)
Fav.add(streetName)
favorite.add(Fav)
} else {
// set deselected
Favoritebutton.isSelected = false
UserDefaults.standard.set(true, forKey: "MY_FAV_KEY")
UserDefaults.standard.synchronize()
let tabItem = self.tabBarController?.tabBar.items![3]
sel_val = tabItem?.badgeValue
if(sel_val == nil){
sel_val = "0"
}
let sel_num = Int(sel_val!)
tabItem!.badgeValue = String(format: "%d", sel_num! - 1) as String
let Fav: NSMutableArray = []
Fav.add(barImage)
Fav.add(barName)
Fav.add(streetName)
favorite.remove(Fav)
}
}
As i said,It will be great if anyone could help me save the button's state
even if i quit the view or app , i would really appreciate it, Thank you in advance !
Saving the value to UserDefaults writes it out to the plist file. It does not read the value back in or restore state when the app resumes. You are responsible for that. So somewhere in viewDidLoad check the state:
let state = UserDefaults.standard.bool(forKey: "isSaved") ?? false
Then set your button accordingly
Related
My vc contains:
-1 name textView
-1 label // for flavors
-2 radio buttons // yes and no
-1 nextButton
What I want to do is keep the nextButton disabled until the textView is filled out, the label's text value is changed from its initial title of "Pick a Flavor" to whatever flavor they pick, and 1 of the radio buttons are selected.
I know using a textView's textViewDidEndEditing() I can listen to changes on it after the user finishes editing disable or enable the nextButton.
func textViewDidEndEditing(_ textView: UITextView) {
handleTextInputChanged()
}
#objc func handleTextInputChanged() {
let isFormValid = nameTextView.text?.count ?? 0 > 0
if isFormValid {
nextButton.isEnabled = true
nextButton.backgroundColor = .red
} else {
nextButton.isEnabled = false
nextButton.backgroundColor = .lightgray
}
}
How would I additionally disable or enable the nextButton based on wether or not one of the radio buttons were selected and the label's text is changed in addition to checking the nameTextView has text inside of it?
FYI the label's text is initially set with "Pick a Flavor". I have a gesture recognizer hooked up to it and when its tapped a new vc with a tableView is presented. The user picks a flavor, a protocol sends it back to this vc and the label's text will change to whatever flavor was selected (eg the label's title would say "Butter Pecan" if chosen). The nextButton should be disabled as long as the label's title is still set to "Pick a Flavor".
code:
let flavorLabel: UILabel = {
let label = UILabel()
label.text = "Pick a Flavor"
label.isUserInteractionEnabled = true
return label
}()
let nameTextView: UITextView = {
let textView = UITextView()
return textView
}()
let noButton: DLRadioButton = {
let button = DLRadioButton(type: .custom)
button.setTitle("No", for: .normal)
button.setTitleColor(UIColor.lightGray, for: .normal)
button.addTarget(self, action: #selector(noButtonPressed), for: .touchUpInside)
return button
}()
let yesButton: DLRadioButton = {
let button = DLRadioButton(type: .custom)
button.setTitle("Yes", for: .normal)
button.setTitleColor(UIColor.lightGray, for: .normal)
button.addTarget(self, action: #selector(yesButtonPressed), for: .touchUpInside)
return button
}()
let nextButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Next", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.isEnabled = false
button.backgroundColor = .lightGray
button.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside)
return button
}()
var choice: Bool?
override func viewDidLoad() {
super.viewDidLoad()
flavorVC.delegate = self
}
func textViewDidEndEditing(_ textView: UITextView) {
handleTextInputChanged()
}
#objc func handleTextInputChanged() {
let isFormValid = nameTextView.text?.count ?? 0 > 0
if isFormValid {
nextButton.isEnabled = true
nextButton.backgroundColor = .red
} else {
nextButton.isEnabled = false
nextButton.backgroundColor = .lightgray
}
}
#objc fileprivate func noButtonPressed() {
choice = false
}
#objc fileprivate func yesButtonPressed() {
choice = true
}
// delegate method from FlavorController
func selectedFlavor(flavor: String) {
flavorLabel.text = flavor // eg. "Butter Pecan"
}
You need to create a function that check all like this
func checkAll() {
nextButto.isEnabled = flavorLabel.text != "Pick a Flavor" &&
nameTextField.text != "" &&
(noButton.someProerty != || yesButton.someProerty != )
}
and call it from textfield and button targets plus
var ob:NSKeyValueObservation?
ob = flavorLabel.observe(\.text,options:[.new], changeHandler: { [unowned self] _, change in
self.checkAll()
}
I left the radio checks because both of them are custom , so you should know what to check , btw you can set tag of selected to 1 inside the target method and check according to it or some other logic you want
In my project I am using 6 buttons in one screen.I want to change the button color based on user's tap and "day order value". i am getting "day order value" from server.for example user day order value is equal to 1, day1 button background color should be red if user taps on day2 button day2 button should be in blue and remaining all button background colour should be white.
Please see the below screenshot.
if user pressed on one button that particular button should be highlight with some color remaining buttons should be same color. I can able to change button color by checking each condition but I want to write in simple manner.
see the following code which i have i tried for "dayoredrvalue".
func UpdateButtoncolor()
{
let dayOrderStr = UserDefaults.standard.string(forKey: "dayOrderStr")
print("dayOrderStr--",dayOrderStr)
if (dayOrderStr?.isEqual("1"))!{
self.day1Btn.backgroundColor = UIColor.red
self.day2Btn.backgroundColor = UIColor.white
self.day3Btn.backgroundColor = UIColor.white
self.day4Btn.backgroundColor = UIColor.white
self.day5Btn.backgroundColor = UIColor.white
self.day6Btn.backgroundColor = UIColor.white
}else if(dayOrderStr?.isEqual("2"))!
{
self.day1Btn.backgroundColor = UIColor.white
self.day2Btn.backgroundColor = UIColor.red
self.day3Btn.backgroundColor = UIColor.white
self.day4Btn.backgroundColor = UIColor.white
self.day5Btn.backgroundColor = UIColor.white
self.day6Btn.backgroundColor = UIColor.white
}else if(dayOrderStr?.isEqual("3"))!
{
self.day1Btn.backgroundColor = UIColor.white
self.day2Btn.backgroundColor = UIColor.white
self.day3Btn.backgroundColor = UIColor.red
self.day4Btn.backgroundColor = UIColor.white
self.day5Btn.backgroundColor = UIColor.white
self.day6Btn.backgroundColor = UIColor.white
}else if(dayOrderStr?.isEqual("4"))!
{
self.day1Btn.backgroundColor = UIColor.white
self.day2Btn.backgroundColor = UIColor.white
self.day3Btn.backgroundColor = UIColor.white
self.day4Btn.backgroundColor = UIColor.red
self.day5Btn.backgroundColor = UIColor.white
self.day6Btn.backgroundColor = UIColor.white
}else if(dayOrderStr?.isEqual("5"))!
{
self.day1Btn.backgroundColor = UIColor.white
self.day2Btn.backgroundColor = UIColor.white
self.day3Btn.backgroundColor = UIColor.white
self.day4Btn.backgroundColor = UIColor.white
self.day5Btn.backgroundColor = UIColor.red
self.day6Btn.backgroundColor = UIColor.white
}else
{
self.day1Btn.backgroundColor = UIColor.white
self.day2Btn.backgroundColor = UIColor.white
self.day3Btn.backgroundColor = UIColor.white
self.day4Btn.backgroundColor = UIColor.white
self.day5Btn.backgroundColor = UIColor.white
self.day6Btn.backgroundColor = UIColor.red
}
}
As per your query, It's quite simple. Just follow the following -
Step 1: Add an UIButton object in your viewController. Like that -
var selectedButton: UIButton? = nil
Step 2: Add same button Action for all your buttons -
#IBAction func btnActionSelection(_ sender:UIButton){
if(selectedButton == nil){ // No previous button was selected
updateButtonSelection(sender)
}else{ // User have already selected a button
if(selectedButton != sender){ // Not the same button
selectedButton?.backgroundColor = .clear
updateButtonSelection(sender)
}
}
}
Step 3: Now, Update button selection
func updateButtonSelection(_ sender: UIButton){
UserDefaults.standard.set("\(sender.tag)", forKey: "dayOrderStr")
selectedButton = sender
selectedButton?.backgroundColor = .green
}
Step 4: Check User selected day (For that you need to add tag on buttons from 1 to 6 respectively)
override func viewDidLoad() {
super.viewDidLoad()
// Check user's selected day
if let selectedDay = UserDefaults.standard.value(forKey: "dayOrderStr") as? String{
debugPrint("selectedDay: "selectedDay) // Get user's selected day
if let btn = self.view.viewWithTag(Int(selectedDay)!){
updateButtonSelection(btn)
}
}
//Other stuff
}
I have attached a demo for you using my logic:
For this you need to take a Group Outlets of your UIButton and assign a unique tag to each and then rest logic is described in the demo attached below.
like
#IBOutlet var btnAllSelected: [UIButton]!
And then simple logic like this:
#IBAction func btnAllClicked(_ sender: UIButton) {
for button in btnAllSelected {
if sender.tag == button.tag{
button.isSelected = true;
button.backgroundColor = UIColor.green
}else{
button.isSelected = false;
button.backgroundColor = UIColor.red
}
}
if sender.tag == 1{
print("Button 1 selected")
}else if sender.tag == 2{
print("Button 2 selected")
}else if sender.tag == 3{
print("Button 3 selected")
}else if sender.tag == 4{
print("Button 4 selected")
}else if sender.tag == 5{
print("Button 5 selected")
}else if sender.tag == 6{
print("Button 6 selected")
}
}
Link to the Demo
FYI:- Please ignore the pods installed in it. I edited some another demo and made a one for you.
Hope this helps.
You should create New Referencing Outlet Collections for all your UIButtons like this,
#IBOutlet var arrButtons: [UIButton]!
Implement only one Button tap events for all your buttons, Please find belo code.
#IBAction func btnClicked(_ sender: UIButton) {
arrButtons.forEach({$0.backgroundColor = UIColor.red})
sender.backgroundColor = UIColor.darkGray
}
Simplified code, it uses an array of the buttons and an index:
let buttonArray = [day1Btn, day2Btn, day3Btn, day4Btn, day5Btn, day6Btn]
buttonArray.forEach { $0.backgroundColor = .white }
guard let dayOrderStr = UserDefaults.standard.string(forKey: "dayOrderStr"),
let dayOrderIndex = Int(dayOrderStr), 1...buttonArray.count ~= dayOrderIndex {
buttonArray[dayOrderIndex-1].backgroundColor = .red
}
It can be still simpler if you save the value in UserDefaults as Int
Here is one solution among many others.
#IBOutlet var buttons: [UIButton]! // link all the buttons from the storyboard
func changeColorButton(sender: UIButton) {
// default appearance for all buttons
for button in buttons {
button.backgroundColor = UIColor.green
}
// update color only for the button clicked
sender.backgroundColor = .orange
}
#IBAction func buttonTapped(_ sender: UIButton) {
updateFocusPicture(sender: sender)
}
I have a TabBar Application and I'm trying to set up a favorites tab.
I have managed to create a button and to send the favorited cell to the tab but right now if i add to favorites they are added correctly into the favorites tab but if i quit the app it doesn't save the favorites . How can i save the favorited cells in the favorite tab? would UserDefault do the best job? and if so how can it be done...
This is how i set up my favorite button :
//create a new button
let Favoritebutton: UIButton = UIButton(type: UIButtonType.custom)
//set image for button
Favoritebutton.setImage(UIImage(named: "EmptyHeart.png"), for: .normal)
Favoritebutton.setImage(UIImage(named: "FilledHeart.png"), for: .selected)
//add function for button
Favoritebutton.addTarget(self, action: #selector(self.button), for: .touchUpInside)
//set frame
Favoritebutton.frame = CGRect(x:0,y: 0,width: 35,height: 35)
Favoritebutton.isSelected = UserDefaults.standard.bool(forKey: "isSaved")
let barButton = UIBarButtonItem(customView: Favoritebutton)
//assign button to navigationbar
self.navigationItem.rightBarButtonItem = barButton
}
#IBAction func button(sender: UIButton) {
let newValue = !sender.isSelected
sender.isSelected = newValue
UserDefaults.standard.set(newValue, forKey: "isSaved")
let tabItem = self.tabBarController?.tabBar.items![3]
sel_val = tabItem?.badgeValue
if(sel_val == nil){
sel_val = "0"
}
let sel_num = Int(sel_val!)
let fav: NSMutableArray = []
fav.add(barImage)
fav.add(barName)
fav.add(streetName)
if sender.isSelected {
tabItem!.badgeValue = String(format: "%d", sel_num! + 1)
favorite.add(fav)
} else {
tabItem!.badgeValue = String(format: "%d", sel_num! - 1)
favorite.remove(fav)
}
}
#Newbie - I think you are not checking, second time when app open what is your favourite tab with your NSUserDefault.
I'm starting to create a favorite button for my project and i need some help...
I have created a button and already programmed it to switch image when tapped right here:
override func viewDidLoad() {
super.viewDidLoad()
//create a new button
let Favoritebutton: UIButton = UIButton(type: UIButtonType.Custom)
//set image for button
Favoritebutton.setImage(UIImage(named: "EmptyHeart.png"), forState: UIControlState.Normal)
Favoritebutton.setImage(UIImage(named: "FilledHeart.png"), forState: UIControlState.Selected)
//add function for button
Favoritebutton.addTarget(self, action: #selector(self.button(_:)), forControlEvents: .TouchUpInside)
//set frame
Favoritebutton.frame = CGRectMake(90, 90, 35, 35)
let barButton = UIBarButtonItem(customView: Favoritebutton)
//assign button to navigationbar
self.navigationItem.rightBarButtonItem = barButton
}
#IBAction func button(sender: UIButton) {
sender.selected = !sender.selected;
}
It works perfectly but as of right now if i quit the app the button does NOT save the status if its selected or not . I heard you can achieve this with NSUserDefault but i don't know how to implement this so i would love to get some help with it :)
I am using Swift 2.3 and Xcode 8.
Thank you.
It's quite easy. In UserDefaults you can store boolean value without any problem.
In viewDidLoad you check value for your key like this.
let defaults = UserDefaults.standard
let buttonIsSelected = defaults.bool(forKey: "keyForIsButtonSelected") // If no value exists for this key then false is returned
button.selected = buttonIsSelected
Inside your button function remember to save your status.
sender.selected = !sender.selected
let defaults = UserDefaults.standard
defaults.set(sender.selected, forKey: "keyForIsButtonSelected")
defaults.synchronize() // Save your defaults, can also move this part of code to applicationWillTerminate
For Swift 2.3 use this.
let defaults = NSUserDefaults.standardUserDefaults()
let buttonIsSelected = defaults.boolForKey("keyForIsButtonSelected") // If no value exists for this key then false is returned
button.selected = buttonIsSelected
Inside button function.
sender.selected = !sender.selected
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(sender.selected, forKey: "keyForIsButtonSelected")
defaults.synchronize() // Save your defaults, can also move this part of code to applicationWillTerminate
for swift 3 it will be next:
//save value
UserDefaults.standard.set(selected, forKey: "lButtonSelected")
//then on next launch restore it
let selected = UserDefaults.standard.value(forKey: "lButtonSelected")
To save the value in NSUserDefaults:
UserDefaults.standard.set(selected, forKey: "buttonStatus")
UserDefaults.standard.synchronize()
And to retrieve the value from NSUserDefaults:
let buttonStatus = UserDefaults.standard.value(forKey: "buttonStatus")
I make simple player with VK Api. I want to do this.
When I clicked first track and then second change first button title "stop" to "play" automatically.
How to do it?
My play/stop button action.
func playAction(sender: UIButton!) {
if sender.currentTitle == "P" {
let track = dataOfTracks[sender.tag] as trackDoc
let url = NSURL(string: track.data.url)
player = AVPlayer(URL: url!)
player.play()
sender.setTitle("S", forState: UIControlState.Normal)
} else if sender.currentTitle == "S" {
sender.setTitle("P", forState: UIControlState.Normal)
player.pause()
}
}
EDIT CODE DETAILS:
don't reallocate every time the button. It must be another outlet of the SongsTableViewCell class (same as the label). And set the target/action from the Interface Builder (add #IBAction in front of "func cellSongClicked" and ctrl-drag from IB)
2.add the following property to your class:private var currentSong : Int?
3) method cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SongTitleCell", forIndexPath: indexPath) as SongsTableViewCell
let songDic : NSDictionary = arrSongs.objectAtIndex(indexPath.row) as NSDictionary
cell.lblSongTitle.text = songDic.objectForKey("SongTitle") as? String
cell.btnPlayPause.tag = indexPath.row
var title : String
if let _currentSong = currentSong {
title = indexPath.row == _currentSong ? "Stop" : "Play"
} else {
title = "Play"
}
cell.btnPlayPause.setTitle(title, forState: UIControlState.Normal)
return cell
}
4) And the action:
#IBAction func cellSongClicked (sender : AnyObject ){
var remote = GetSongData()
remote.delegate = self
var btnCurrentPressed = sender as UIButton
//Play Selected Song
let songDic : NSDictionary = arrSongs.objectAtIndex(btnCurrentPressed.tag) as NSDictionary
var rowsToReload = [NSIndexPath]()
var stopCurrent = false
if let _currentSong = currentSong {
if btnCurrentPressed.tag == _currentSong {
stopCurrent = true
} else {
rowsToReload.append(NSIndexPath(forRow: _currentSong, inSection:0))
}
}
rowsToReload.append(NSIndexPath(forRow: btnCurrentPressed.tag, inSection:0))
currentSong = stopCurrent ? nil : btnCurrentPressed.tag
self.tableView.reloadRowsAtIndexPaths(rowsToReload, withRowAnimation: .None)
}
You don't have to change the title of the button every time. You could use something like this:
func viewDidLoad {
playButton.setTitle("Play", forState: UIControlState.Normal)
playButton.setTitle("Stop", forState: UIControlState.Selected)
}
Then in your callback method you just change the state of the button and the text will get updated too:
func playAction(sender: UIButton!) {
// Selected means the content is playing
if !sender.selected {
let track = dataOfTracks[sender.tag] as trackDoc
let url = NSURL(string: track.data.url)
player = AVPlayer(URL: url!)
player.play()
} else {
player.pause()
}
// update the state of the button
sender.selected = !sender.selected
}
I consider this to be a more clean solution since you are not comparing strings anymore to make your decision and you are actually using the binary state of the button to keep track of the current state of your content(which is also binary, either playing or not).
If you have more questions, I would be more than happy to help you further, just let me know.