Switch Save user preference - ios

I am trying to send a tag with OneSignal when the switch is turned on, and send request to delete the tag when it is turned off again.
#IBAction func tagGeneral(_ sender: UISwitch) {
if (sender.isOn == true) {
OneSignal.sendTag("General", value: "value")
print("sendtag")
}
else {
OneSignal.deleteTag("General")
print("deletetag")
}
}
This is the code i use for it. Seems to be working but when the user goes to another page the switch is automatically turned off...
How can i fix this?

Regarding #Ryan's comment, Here's an answer:
First. there are many ways to save the user preference, i'll be doing it with UserDefaults() | Edit your button action code:
#IBAction func tagGeneral(_ sender: UISwitch) {
let userdef = UserDefaults.standard
if (sender.isOn == true) {
OneSignal.sendTag("General", value: "value")
print("sendtag")
// user made the choice
userdef.set(true, forKey: "sw_set")
} else {
OneSignal.deleteTag("General")
print("deletetag")
// reset
userdef.set(false, forKey: "sw_set")
}
}
Normally this wouldn't work without this little function, make sure you call this function in your viewDidAppear():
private func init_switch() {
// Thanks #Vadian for the tip
let userdef = UserDefaults.standard
self.yourSwitch.isOn = userdef.bool(forKey: "sw_set")
}
Call it in viewDidAppear():
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.init_switch()
}
And let me know if it helped.

Related

Using multiple toggles I'would like to regroup all toggles in one action

I'm trying to create an array composed of multiple tools.
The array changes depending on which tools are carried.
Expecting a long list of tools I would like to avoid having to write an action for each single element to add or remove from the array. ( I managed this part as you'll see below)
Instead I'd like to use the name of the toggle switch and apply the same action to all the toggles on the screen.
I came up with this inelegant method:
import UIKit
class mainVC: UIViewController {
var myEquippement: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func shovelSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Shovel")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Shovel")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func hammerSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Hammer")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Hammer")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func screwdriverSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Screwdriver")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Screwdriver")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func sawSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Saw")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Saw")
myEquippement.remove(at: subjectIndex!)
}
}
}
could you point me please to a better way of doing it.
I thought of using something like this:
#IBAction func toggleSwitched(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append(sender.title!)
}
else {
let subjectIndex = myEquippement.firstIndex(of: sender.title!)
myEquippement.remove(at: subjectIndex!)
}
}
but sender.title always returns a nil value, the force unwrapping crashes the app.
Thanks in advance for your help.
One solution would be to have a master array of tool names. When creating a switch, assign its tag property to the corresponding index within that array.
Then in your single switch handler you can use the sender's tag to get the name of the tool from the master list of tool names. That will give you the title for the switch. Then your logic for adding/removing the title from myEquipment can be used.

Create new event in CalendarKit

In my app, I'm trying to add ability to create a new event with CalendarKit when I press an empty space, but I can't find how to do that.
I know it's possible because you can do it in the demo app:
I've tried adding this to my app's code:
override func create(event: EventDescriptor, animated: Bool = false) {
self.events.append(event) // self.events is my events data source
}
But it didn't worked, in fact, it doesn't even get called when I long press an empty space.
I also tried to look in the source code, but I found nothing. How can I do that? thanks in advance
override func dayView(dayView: DayView, didLongPressTimelineAt date: Date) {
let newEvent = Event()
newEvent.startDate = date
newEvent.endDate = date.addingTimeInterval(3600)
// Customize your event...
newEvent.text = randomName() // A function that generates a new random name that haven't been used before.
self.create(event: newEvent)
}
override func create(event: EventDescriptor, animated: Bool = false) {
super.create(event: event, animated: animated)
self.events.append(event)
}
override func dayView(dayView: DayView, didUpdate event: EventDescriptor) {
for (index, eventFromList) in events.enumerated() {
if eventFromList.text == event.text {
events[index] = event
}
}
self.endEventEditing()
self.reloadData()
}
Please make sure that every Event have it's own unique name, otherwise it won't work

How to add an alert to switch toggle for multiple answers?

I created a Quiz app. There is one question with five answers, where the user can choose one of them. However at the moment it's possible to choose more than one answer. How can I add an alert (UIAlertController) and a restriction so if the user has already chosen one answer and tries to choose a second one ?
var questionIndex = 0
var answersChosen: [Answer] = []
func updateUI() {
let currentQuestion = questions[questionIndex]
let currentAnswers = currentQuestion.answers
let totalProgress = Float(questionIndex) /
Float(questions.count)
numberOfQuestion.text = "\(questionIndex+1)/5"
// navigationItem.title = "Вопрос - \(questionIndex+1)"
mainQuestion.text = currentQuestion.text
progressBar.setProgress(totalProgress, animated:
true)
updateMultipleStack(using: currentAnswers)
}
#IBAction func multipleAnswerButtonPressed(_ sender: Any) {
let currentAnswers = questions[questionIndex].answers
if firstSwitch.isOn {
answersChosen.append(currentAnswers[0])
}
if secondSwitch.isOn {
answersChosen.append(currentAnswers[1])
}
if thirdSwitch.isOn {
answersChosen.append(currentAnswers[2])
}
if fourthSwitch.isOn {
answersChosen.append(currentAnswers[3])
}
if fifthSwitch.isOn {
answersChosen.append(currentAnswers[4])
}
nextQuestion()
}
func updateMultipleStack(using answers: [Answer]) {
firstSwitch.isOn = false
secondSwitch.isOn = false
thirdSwitch.isOn = false
fourthSwitch.isOn = false
fifthSwitch.isOn = false
firstQuestionLbl.text = answers[0].text
secondQuestionLbl.text = answers[1].text
thirdQuestionLbl.text = answers[2].text
fourthQuestionLbl.text = answers[3].text
fifthQuestionLbl.text = answers[4].text
}
override func prepare(for segue: UIStoryboardSegue, sender:
Any?) {
if segue.identifier == "ResultSegue" {
let resultsViewController = segue.destination as! ResultViewController
resultsViewController.responses = answersChosen
}
}
}
The approach I'd take would be to separate the user's selection of an answer (UISwitch collection) from processing the selected answer(Submit Answer button). This would allow the user to change their answer. The previous selection is automatically cleared. The answer is final when pressing a "Submit Answer" button (not shown).
In this code example, the switches are set as an IBOutlet collection. The tag on each switch is set from 0 to 4. All the switches are connected to an IBAction called switchPressed. A variable called selectedAnswer is used to identify the user's final picked answer when the "Submit Answer" is pressed.
#IBOutlet var switches: [UISwitch]!
private let noAnswer = -1
private var selectedAnswer = -1
#IBAction func switchPressed(_ sender: UISwitch) {
// Check if user turned off switch
guard sender.isOn == true else {
selectedAnswer = noAnswer
return
}
// Get user's answer and set all switches
selectedAnswer = sender.tag
switches.forEach { $0.isOn = false }
sender.isOn = true
}
#IBAction func submitAnswer(_ sender: UIButton) {
guard selectedAnswer > noAnswer else { return }
// use selectedAnswer to process answer and go to the next question
answersChosen.append(currentAnswers[selectedAnswer])
}
You could store all the switches in an array named Switches for example and then run a for loop to check if more than one is selected when checking answer.
var count = 0
for switchToggle in switches {
if switchToggle.isOn {
count += 1
if count > 1 {
//Print Warning "only select one"
}
} else {
//append answers
}
}
you could also use this to have multiple answers for certain quiz question
For the warning, you have different options you could use. You could use UIAlertController to display a warning if more than one button is selected but this could become annoying for the user after a while.
Another option could be to use a UILabel which warns the user to only pick one answer. You could animate the label to fade away after a few seconds.

I keep getting the errors... 'use of unresolved identifier' and 'Type gameType has no member pause game'

I created a button to pause my game and I'm trying to make some actions for it.
So I have no idea what I'm doing wrong, all other 'solutions' on this website haven't been working for me, so could you please tell me what I'm doing wrong, or rather what I'm not doing.
import Foundation
import UIKit
enum gameType {
case easy
case medium
case hard
case player2
case impossible
}
func pauseGame() { // HERE IS WHERE I GET THE ERRORS... IN THIS FUNCTION
self.isPaused = true
currentGameType = gameType.pauseGame
self.physicsWorld.speed = 0
self.speed = 0.0
}
class MenuVC : UIViewController {
#IBAction func Player2(_ sender: Any) {
moveToGame(game: .player2)
}
#IBAction func Easy(_ sender: Any) {
moveToGame(game: .easy)
}
#IBAction func Medium(_ sender: Any) {
moveToGame(game: .medium)
}
#IBAction func Hard(_ sender: Any) {
moveToGame(game: .hard)
}
#IBAction func Impossible(_ sender: AnyObject) {
moveToGame(game: .impossible)
}
func moveToGame(game : gameType) {
let gameVC = self.storyboard?.instantiateViewController(withIdentifier: "gameVC") as! GameViewController
currentGameType = game
self.navigationController?.pushViewController(gameVC, animated: true)
}
}
At the top of your file, gameType is an enumeration. That means that there are a finite set of cases that it can have (the game type can either be easy, medium, hard, etc.) In your code, you're attempting to use a case called pauseGame, but if you look at the definition of gameType there's no case with that name. So you either need to a) add a pauseGame case to gameType (does it make sense for pauseGame to be a "kind" of game?) or not set the gameType inside your pauseGame function.
My instinct is that you don't actually want to change the type of the game when the user pauses it, so you can probably just get rid of that line. Also, a note on style -- it's good to start the names of types with capital letters to distinguish them from functions and local variables (so GameType instead of gameType.)

Display coach marks view when user launches app for the first time Swift 3

I need to launch a coach marks/tutorial view over a tableview when the user launches the app for the first time. The coach marks will never display again, unless the user deletes and reinstalls the app.
I scoured the internet, but was unable to find a simple solution. Oh, I'm fairly new to Swift, so please be gentle. :)
EDIT:
Here is the what I used. Works great!
override func viewDidAppear(_ animated: Bool) {
if !UserDefaults.standard.bool(forKey: "isSecondTime") {
let launchedBefore = UserDefaults.standard.bool(forKey: "isSecondTime")
if launchedBefore {
print("Not the first launch.")
} else {
let VC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CoachMarksViewController")
self.present(VC, animated: true, completion: nil)
print("This the first launch.")
UserDefaults.standard.set(true, forKey: "isSecondTime")
}
}
}
Probably this is what you need?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated);
let kFirstLaunch = "First_launch"
if (UserDefaults.standard.object(forKey: kFirstLaunch) == nil) {
UserDefaults.standard.set(1, forKey: kFirstLaunch);
//Show your coach marks here
}
}
You should use "UserDefaults" to achieve this. In your "viewDidLoad" method check if a value let's say "isSecondTime == true" (This value you will access from "UserDefaults") then do nothing otherwise in else section show your tutorial and save the value "isSecondTime = true" in "UserDefaults". It will work according to your requirements. Check this code:
if(NSUserDefaults.standardUserDefaults().boolForKey("isSecondTime"))
{
// app already launched
}
else
{
// This is the first launch ever
// show your tutorial.....!
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isSecondTime")
NSUserDefaults.standardUserDefaults().synchronize()
}
For Swift 3:
let launchedBefore = UserDefaults.standard.bool(forKey: "isSecondTime")
if launchedBefore {
print("Not first launch.")
} else {
// // show your tutorial.....!
print("First launch, setting UserDefault.")
UserDefaults.standard.set(true, forKey: "isSecondTime")
}
You should use this little launches manager :)
enum AppLaunchManager {
// MARK: - Private Attributes
private static let launchesCountKey = "LaunchesCount"
// MARK: Public Enum Methods
static func launchesCount() -> Int {
guard let launched = UserDefaults.standard.value(forKey: launchesCountKey) as? Int else {
return 0
}
return launched
}
static func registerLaunch() {
UserDefaults.standard.setValue(launchesCount() + 1, forKey: launchesCountKey)
}
static func isFirstLaunch() -> Bool {
return launchesCount() <= 1
}
}
And in AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
AppLaunchManager.registerLaunch()
return true
}
Then, whenever you need just use:
if AppLaunchManager.isFirstLaunch() {
/* do whatever you need */
}
So you need a bool to indicate whether to show the coach marks/tutorial that persists across the app being terminated and restarted. The first time the app is run the bool will indicate that the coach marks must be displayed then the app will set the bool to indicate that they are no longer required.
To persist the bool you could use UserDefaults to store the bool and read it when the app starts. Here is the link to the Apple documentation on it.
p.s. it might be nice to give the user a way to display them again as they might forget something or just dismiss them without thinking.

Resources