making and presenting app tutorial views in Xcode - ios

I have an app that is not super intuitive. I want to take screenshots of the confusing screens and then use MSpaint to write instructions and doodles. When the user opens the view in the app for the first time, I want to present the series of altered screenshots along with an "OK" button. pressing OK will dismiss the screenshot and it will not be shown again. Is there an efficient way to do this? I am new to Swift and Xcode. Any help would be appreciated

You need to use NSUserDefaults to save the state of the app (tutorial shown or not) - NSUserDefaults saves data between the runs of the app to the device storage.
Then you need to change AppDelegate to change the initial view controller according to the value you saved - that way if tutorial has been shown it will not show again.
Assuming you have var called toturialShown
Set it to false and each run check its value to determine if the tutorial needs to be shown
When the user taps on dismiss tutorial button use NSUSerDefaults to save this new status
Store
UserDefaults.standard.set(toturialShown, forKey: "toturialShownKey")
Retrieve
UserDefaults.standard.bool(forKey: "toturialShownKey")
Remove - in case you want to delete it completely from storage
UserDefaults.standard.removeObject(forKey: "toturialShownKey")
On AppDelegate in applicationDidFinishWithOptions function
(Note that I didn't test the code)
var vc = ""
If toturialShown {
vc = "regularVC"
} else {
vc = "toturialVC"
}
let initialViewController = mainStoryboard.instantiateViewController(withIdentifier: vc)
let initialViewController = mainStoryboard.instantiateViewController(withIdentifier: "LoginSignupVC")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
Note1: you need to add identifiers to you VCs in storyboard
Note 2: if you set your initial VC (on storyboard) to the regular VC, you can change the code above to set the initial VC programmatically only if the tutorial has not been shown, results in slightly more elegant code

Related

Add and Edit Data using Single View Controller?

I am a beginner, I am trying to add and edit data using a single view controller, process works fine, but when I try to click add button, view controller is pushed twice.
When I try to Open Click
But When I click Back in the navigation bar, the Screen appears with my designs.
Code For Launching Add Screen
let MenuAdd = self.storyboard?.instantiateViewController(identifier: "MenuEdit") as! AdminMenuVC
MenuAdd.IsEdit = true
self.navigationController?.pushViewController(MenuAdd, animated: true)
Code For Launching Edit Screen
let MenuEdit = self.storyboard?.instantiateViewController(identifier: "MenuEdit") as! AdminMenuVC
MenuEdit.IsEdit = true
self.navigationController?.pushViewController(MenuEdit, animated: true)
There are possible explanation for this:
You also have a storyboard segue going from the add button to the AdminMenuVC- if this is the case, just delete it (you are already pushing it from code)
You call from the first VC both self.navigationController?.pushViewController(MenuAdd, animated: true) and self.navigationController?.pushViewController(MenuEdit, animated: true) - they both refer to the same VC (AdminMenuVC). I cannot say if this is the case as you haven't posted the code. Be careful to check that you are not calling the two lines of code at the same time
Check button action you may be setting action and touch up inside actions on same button. You need to remove one action from reference inspector

How can I push two VC but only presenting top VC

I would like to emulate contact list in ios. When one taps + button to add contact, a new view controller is presented that gives you textfields to enter contact name and additional info for saving about that contact. Once you tap Done button, the next view controller presented already seems to be embedded in a navigation controller with the button that takes you back to the contact list. I've tried code found on here but it pushes 3 view onto navigation stack
//first attempt
var controllers = navigationController?.viewControllers
controllers?.append(secondVc)
controllers?.append(thirdVC)
navigationController?.setViewControllers(controllers!, animated: true)
//second attempt
let pushVC = UIViewController()
let backVC = UIViewController()
if let navigationController = navigationController {
navigationController.pushViewController(pushVC, animated: true)
let stackCount = navigationController.viewControllers.count
let addIndex = stackCount - 1
navigationController.viewControllers.insert(backVC, atIndex: addIndex)
}
I've also tried other combinations that look cringy. Here is what I want it to look like: https://imgur.com/a/IAJ5G
This should work:
navigationController.pushViewController(secondVc, animated:false)
navigationController.pushViewController(thirdVc, animated:true)
Apple does the same by presenting a new window, with the form to create a new contact.
After making the new window as the keyWindow, they push the contact details VC on to the navigation controller on the original window. This push happens in the background, which the user is not able to witness.
You can view the same by attaching a debugger to the Contacts app.
Heres a screenshot of the view hierarchy. You will be able to see that there are no view controllers from the original window underneath the CNContactContentViewController.
When the user taps Done, the original window is restored as the keyWindow, and the contact details VC is updated to show the newly added contact.

How can I manage and free memory through ViewControllers

I'm in front of a big issues, and the only reason I can't find a solution is because my lack of knowledge about swift and memory management in swift. So here is my concerns. I'm working in swift 4.0 and iOS 9.3
I'm actually making a picture gallery app with login/logout. Basic application.
I'm working in cleanSwift So I don't have those HUGE ViewControllers.
My application is separate in 3 VC : The login, the gallery and the settings (which contains the LogOut).
Here is my problem. When I log out, I want to create a new loginVC and clear all previous VC.
So I have my cleanMemory function which set all the UIImage to nil
func cleanMemory(request: Gallery.Request) { // Interactor
worker.cleanMemory(completionHandler: { (Value) in
self.interventions?.removeAll() // Interventions contains UIImages
self.interventionsSelected.removeAll() // InterventionsSelected contains UIImages
})
}
and then I delete the rests of the UIImage and the VC
func cleanMemory() {
interactor?.cleanMemory(request: Gallery.Request())
self.displayedInterventions.removeAll() // displayedInterventions contains UIImages
interactor = nil
router = nil
self.removeFromParentViewController()
self.navigationController?.popViewController(animated: true)
}
But when I create my new LoginVC.. my RAM didn't decrease.. And when I check the app memory, not a single VC was deleted.. And when I execute the loop Logout/Login 3 times, my app crash because I'm not well managing my RAM_
So where did I get wrong, and why ??
Thanks you for your answer.
EDIT: I was having 2 problems :
My completionHandler was keeping my VC alive
I was switching VC with .present, so that was keeping my VC in memory.
So you should change VC like that :
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyboard.instantiateViewController(withIdentifier: "LoginController")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = destinationVC
To remove viewController from memory you simply need to remove it from navigation stack. So when you call navigationController?.popViewController(animated: true) and back to previous view controller you already destroy that controller.
Then,
Here is my problem. When I log out, I want to create a new loginVC and clear all previous VC. So I have my cleanMemory function which set all the UIImage to nil
on logout it's good practice to stop all request but you don't need to do any changes to UI, because it takes some time and it doesn't need to "remove controller from memory". How to check if view controller completely removed from navigation stack? Simply write print statement in deinit func, compile code and go back from this view controller.
deinit {
print("ViewController deinit")
}
If this print works fine (you can see text in xcode console), you achieve the result - controller has been removed from navigation stack, but if there is no print result you probably forget to right manage your closures. For example
worker.cleanMemory(completionHandler: { (Value) in
...
})
this closure may hold your controller when your think that controller already deallocated and it means that your controller present somewhere in memory. To prevent these retain cycles you need to use [unowned self] of [weak self] (just google for this keywords, it's very easy to understand) like this:
// or you can use `[unowned self]`
worker.cleanMemory(completionHandler: { [weak self] (Value) in
guard let `self` = self else { return } // only for `weak` way
...
})
So, in this case there are nothing that can hold your controller alive after pop from navigation stack action or so.
These are simple rules that you should follow to write well managed code.

swift3 how to show navigation viewcontroller

I would like to open the specific view from the notification widget.
normally other answers are create new view..(let viewcontroller = ... )
so If you continue to call, continue use memory..
So, I want to open viewA, which is already opened in my app, and then move to viewB.
My application structure is basically a viewA when the application is launched.
What I think is that instead of creating a new viewA, I want to move to viewB using the navigation.push from the already existing viewA.
The way to check whether a particular page already exists, how to show that page at the top , and how to navigation.push is work.
now i'm using this code to open viewA from Appdelegate
let MainView: Main_View_List_ = mainStoryboard.instantiateViewController(withIdentifier: "Main_List_View") as! Main_View_List_
let nav = UINavigationontroller.init(rootViewController: MainView)
UIApplication.topViewController()?.present(nav, animated: true, completion: nil)
Assuming you have access to the navigation controller, you'll want to do something like this to pop back to viewA:
for viewController in nav.viewControllers {
if viewController is ViewAClass {
viewController.pushViewBFlag = true
nav.popToViewController(viewController, animated: true)
return
}
}
Once you get back to viewA, you can check for pushViewBFlag and create and push viewB.
Alternately, since it seems that you are setting viewA as the root of your navigation controller, you could do: nav.popToRootViewControllerAnimated(true) to get back to viewA. You would need to then handle pushing viewB from viewA.

How to open app based on last viewController used in Swift?

I currently have a quiz game app where each round is represented by a different view controller. When the user closes the app and later re-opens the app I want it to keep the same score and stay on the same viewcontroller it was last on. I believe NSUserDefaults is the way to go but not sure how to set it up to remember the last round it was on. Any help will be appreciated!
You can create an user defaults object using:
// Create an object reference to the NSUserDefaults class
let defaults = NSUserDefaults.standardUserDefaults()
you can request and set objects for this NSUserDefaults class which will be persistent upon closing and opening the app.
// Set a string for a key
defaults.setObject("a string", forKey: "myVar")
// Request the object associated with the key:
let myVar : String = defaults.stringForKey("myVar")
You can save and update the value for the object whenever you feel like saving it. If you save the StoryBoard ID of the View Controller you want to present when starting the app then you can reopen it whenever the app appears using:
// Create a reference to the Storyboard file
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
// Create a reference to the ViewController you want to open
let vc : UIViewController = storyboard.instantiateViewControllerWithIdentifier("ID FROM NSUserDefaults")
// Present the ViewController
dispatch_async(dispatch_get_main_queue(), {
self.presentViewController(vc, animated: true, completion: nil)
})

Resources