Change View with NavigationViewController - ios

all this is probably a trivial question, but I have not found a solution to it. I am making an app for Iphone using Swift.
I have a tableview with some strings and if I press a button I want to navigate back to the previous view directly. However, the code after my call
navigationController?.popViewControllerAnimated(true)
is always run, but I want the current activity to stop and go back to the previous view.
The code looks like:
#IBAction func DeletePressed(sender: UIButton) {
let deleteIndices = getIndexToDelete()
navigationController?.popViewControllerAnimated(true)
print("After navigationController")
for index in deleteIndices{
results?.ListResults[yearShownIndex].months[monthShownIndex].day[dayShownIndex].results.removeAtIndex(index)
}
if (results?.ListResults[yearShownIndex].months[monthShownIndex].day[dayShownIndex].results.count == 0){
results?.ListResults[yearShownIndex].months[monthShownIndex].day.removeAtIndex(dayShownIndex)
}
if (results?.ListResults[yearShownIndex].months[monthShownIndex].day.count == 0){
results?.ListResults[yearShownIndex].months.removeAtIndex(monthShownIndex)
}
if (results?.ListResults[yearShownIndex].months.count == 0){
results?.ListResults.removeAtIndex(monthShownIndex)
}
loadView()
}
"After navigationController" is always displayed.
In android you would start a new activity by creating intents to get the desired behaviour, but how does it work on Iphone?
My problem is that I want to be able to go back directly when navigationController.popViewControllerAnimated is called. This is just a toy example to understand how it works so that I can use it in the if-clauses later.

you could simply add a return statement after you pop the viewcontroller:
#IBAction func DeletePressed(sender: UIButton) {
let deleteIndices = getIndexToDelete()
navigationController?.popViewControllerAnimated(true)
return;
[...]

if you don't wants to execute code after "print("After navigationController")" then remove that code
or it is not possible to remove then toggle it when DeletePressed called

Related

Segue is not working, and i can't figure out why

I have an if...then statement in the ViewDidLoad method for the view that acts as my storyboard entry point.
Basically, I am doing a check to see if there is any data in core data, to indicate that they've completed a small "setup form".
If it is found that the core data is empty or that the app has not been properly set up, I want it to automatically kick them over to the settings view with a segue.
My ViewDidLoad method looks like this:
override func viewDidLoad() {
super.viewDidLoad()
//Get any entries from the App Settings Entity
getAppSettings()
//If any entries are found, check to see if the setup has been completed
if (appSettings.count > 0) {
print("We found entries in the database for App Settings")
if (appSettings[0].setupComplete == false) {
print("Setup has not been completed")
self.performSegue(withIdentifier: "toAppSettings", sender: self)
} else {
print("Setup is completed")
//Load the settings into global variables
preferredRegion = appSettings[0].region!
usersName = appSettings[0].usersName!
}
} else {
print("We found no entries in the database for App Settings")
self.performSegue(withIdentifier: "toAppSettings", sender: self)
}
}
I made sure that the segue does exist, and that the identifier for the segue is exactly as I have it in the quotes (I even copied & pasted all instances of it to make sure that they are all consistent).
I also went the extra mile and put a checker in the "prepare for segue" method, to print whether it was getting called, and who the sender was:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("We're getting ready to segue!")
print(segue.identifier)
}
Both of those items get printed to the log, which tells me that the segue is being recognized and that the app is attempting to fire it. But - for some reason that I can't figure out - it simply isn't firing.
Any ideas what I am missing here?
I have an if...then statement in the ViewDidLoad
But that's the problem. viewDidLoad is way too early for this. Remember, all viewDidLoad means is that your view controller has a view. That's all it means. That view is not yet in the interface, and the view controller itself may not even be in the view hierarchy! It's just sitting out there in object space. You cannot segue from here; there is nothing to segue from.
Try waiting until viewDidAppear. If that makes it work, you might try moving it back to viewWillAppear, but I don't guarantee anything. Keep in mind that these methods can be called multiple times, so you might also need a flag to make sure that this segue fires just the once.

Swift 3: Checking Internet (Reocurring) viewDidAppear not

Ok, So I am rather new to Swift and I am a little confused about what I am trying to do, or if I am going in the wrong direction. (https://github.com/ashleymills/Reachability.swift)
Here is my viewDidLoad method :
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
checkConnection()
}
I then have a function with the code in from the Reachability GitHub Project:
func checkConnection() {
//declare this property where it won't go out of scope relative to your listener
let reachability = Reachability()!
reachability.whenReachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
if reachability.isReachableViaWiFi {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
}
}
reachability.whenUnreachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
self.dim(direction: .In, alpha: self.dimLevel, speed: self.dimSpeed)
self.performSegue(withIdentifier: "mainToAlertNoInternet", sender: self)
}
}
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
}
As you can see, when there is no internet, this is the code:
self.dim(direction: .In, alpha: self.dimLevel, speed: self.dimSpeed)
self.performSegue(withIdentifier: "mainToAlertNoInternet", sender: self)
The Dim is taken from (http://www.totem.training/swift-ios-tips-tricks-tutorials-blog/ux-chops-dim-the-lights) the mainToAlertNoInternet loads the next view over the current one with transparence so it is an alert style.
So, the second segue has a view and a button on it. Nothing spectacular this is what is loaded when there is no internet:
That try again button is linked to the Exit of the Segue and runs this function in the First View Controller:
#IBAction func unwindFromSecondary(segue: UIStoryboardSegue) {
dim(direction: .Out, speed: dimSpeed)
checkInternetConnection()
}
I added in the function mainToAlertNoInternet so that when they click try again, it will go back to the first segue and run the test again. However, When I click try again, I get this error:
Warning: Attempt to present on whose view is not in the window hierarchy!
Hopefully I have explained enough what I have set up. Now to the Questions:
1) How can I fix this error?
2) Am I doing this the right way or is there a better way?
This is what I want:
I want to check the internet connection the moment the app loads. If there is no connection I want to display the segue like I have been doing. If the user clicks Try gain, I want it to go back to the first controller and run the check again and if still no connection display the segue like it did initially again. I would like this to be a repeating process until there is internet.
Appreciate all your help. Thanks in advance
Edit:
I have added the function call in the ViewDidAppear method like so:
override func viewDidAppear(_ animated: Bool) {
checkInternetConnection()
}
However, it does not run. Also the DIM in the unwindFromSecondary function does not get called when I do this.
Edit 2:
Just added this line into my viewDidAppear:
print("Appeared")
This gets called initially, but then not again.
Question:
How do I get a function to run once everything has loaded again after the unwindSegue?
Any thoughts?
Update 3
Ok, so I have looked at the answers below:
#MarkP Answer works fine. Thank you for that
However, the answer from #iSee has got me thinking maybe I should be going about this a different way.
I have added a Bounty to this post for a detailed answer that can show me and explain how to achieve the following:
In my app. I need to keep making sure that the internet exists (Maybe a Timer) on any view that loads. I would like it so that like the current way, If there is no internet it will pop up the ViewController with this segue:
performSegue(withIdentifier: "mainToAlertNoInernet", sender: self)
It appears that the App Delegate would be the place but I am unsure how to achieve this.
I am new to iOS Development and thus would appreciate some explanation and teaching.
Thank you for your time. It is greatly appreciated.
I presume you are new to iOS development in general.
The warning has nothing to do with your internet connectivity code.
The Warning you are getting is not an error. It is just that, a warning.
The reason you are getting that is well explained in this link and this
To get rid of that warning, you should not call performSegue from viewDidLoad(refer to the above link for more information.
To perform your network checks, it is advisable to use the AppDelegate (gives you a better control over the flow of the app)
All the best :)
EDIT: Please refer to this link here for more information on this. I could easily repost it here but since it is already answered, you can refer to the above link as to why it happens and how to avoid it.
As #iSee has pointed out with the links. This is because the view has not been added to the view hierarchy so you can't move to it. for this self.dismissViewControllerAnimated is needed:
#IBAction func unwindFromSecondary(segue: UIStoryboardSegue) {
dim(direction: .Out, speed: dimSpeed)
self.dismiss(animated: true) {
self.checkInternetConnection()
}
}
I use this in the app delegate ->
func reachablityCode() {
AFNetworkReachabilityManager.sharedManager()
AFNetworkReachabilityManager.sharedManager().startMonitoring()
AFNetworkReachabilityManager.sharedManager().setReachabilityStatusChangeBlock({(status) in
let defaults = NSUserDefaults.standardUserDefaults()
if status == .NotReachable {
defaults.setBool(false, forKey:REACHABLE_KEY)
}
else {
defaults.setBool(false, forKey: REACHABLE_KEY)
}
defaults.synchronize()
})
}
And then this in the base file ->
func isReachable() -> Bool {
return NSUserDefaults.standardUserDefaults().boolForKey(REACHABLE_KEY)
}

Xcode with swift: Is it possible to show a view controller upon another without resetting the first?

I'm creating a simple game with a pause button. I want to show another view controller with the pause screen without resetting the game view controller when i return. Every thing i have tried, has resulted in the game view controller being reset, when i press the resume button and return to the game.
Does anybody know how i can do this without resetting the game?
It's a sprite kit and swift iOS app in xcode.
You can show a pop-up based view. You can create .xib and design your pause view as you wish. Then you can resume the game without resetting anything.
Sample code:
// Set Pause Page
var pauseView = PauseView.loadFromNib() // need an extension
#IBAction func Pause(sender: UIButton) {
pauseView.btnResume.addTarget(self, action: #selector(resume(_:)), forControlEvents: .TouchUpInside)
// set place on view
view.addSubview(pauseView)
}
func resume(sender:UIButton) {
pauseView.removeFromSuperview()
}
I created a demo for you:
Full demo available on Github: Get Source Code
Create a variable in your original view controller:
var shouldReset = true
Then, when you perform a segue back, add this in your prepareForSegue:
let vc = destination as! //Your view controller
vc.shouldReset = false
In your viewDidLoad (or wherever you reset your data):
if shouldReset == true {
//Reset
}

Thread 1: EXC_BAD_ACCESS (code=2, address=0x7fff59d8dff8)

What I'm trying to do:
The following bit of code is for 2 buttons on a page. This page is part of an array of pages (which is stored in self.parent; it also stores the index it has in that said array in self.index).
The 2 buttons are used to navigate forwards and backwards in this array of pages. If it tries to go forward but hits the end of the array, it should go back to the first page; Likewise for going back and hitting the beginning of the array.
#IBAction func previousButton(sender: AnyObject) {
self.prev()
}
#IBAction func nextButton(sender: AnyObject) {
self.next()
}
func next(){
if let _parent = self.parent{
if self.index != _parent.getSize()-1 {
self.view.addSubview(_parent[self.index+1].view)
}
else{
self.view.addSubview(_parent[0].view)
}
}
else{
println("This Page is not part of a List")
}
}
func prev(){
if let _parent = self.parent{
if self.index != 0 {
self.view.addSubview(_parent[self.index-1].view)
}
else{
self.view.addSubview(_parent[_parent.getSize()-1].view)
}
}
else{
println("This Page is not part of a List")
}
}
Problem is:
EDIT: The crashes have nothing to do with the if statements and it isn't random either; It crashes when I hit the next button for the tenth time, the previous button for the tenth time (ten is the number of pages I have in my list; if I add more pages, this number changes accordingly), and when I press prev after pressing next OR pressing next after pressing next.
I figured out the problem: I am not properly switching between pages, I am just adding one on top of the others; The crash occurs when I attempt to show a page that was already loaded (and still is somewhere beneath the stack of pages).
Now I need to find a better way to switch between them.
Make sure you have the same class name in StoryBoard and in ViewController

Show a loading animation with swift while waiting for the data from Parse

I've browsed through many q&a-s here but almost all of them are way too specific for the use cases of other people.
My situation is more general and sort of simple:
iOS\Swift
I have a button which when clicked - moves the user to the next view.
At the same time as the button is clicked it also executes a query to Parse to fetch the data which will be displayed on the next view.
I'm using Parse's async query.getObjectInBackgroundWithId("jjjkkkdddd")
So if my code runs as is = click -> move to the next view -> empty
because fetching stuff takes a second or so.
What i want is to have a small animation popping up when user clicks a button to tell them that the data is being fetched at the moment and move to the next view once data arrives.
Here is my button tap code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "1") {
var svc = segue.destinationViewController as ViewControllerQuotes;
svc.toPass = functionToFetchDataFromParse() //<- takes longer than
//switching to the next view
//toPass is a var which lands into the next view
//and it's value is displayed to the user.
}
}
I am quite fresh with Swift and iOS dev so I can't figure this one out still:(
On the button tap just use addSubview() to add a UI element like UIActivityIndicatorView.
In the callback from Parse, remove the view and go to the next controller, calling the segue programatically.
SOLUTION FOR ME:
Ok i fiddled around and found the best way which suites me for now. what I do is from View1 the button is pressed and it tell the view controller of the 2-nd view to fire up the data fetching function.
In the 2-nd vie controller once it trigger the async function for retrieving data from Parse it also shows the activity indicator and make it run.
The user sees an empty screen with an activity spinner running. once the async function gets back the data it pushes it into a an empty label and shutsdown+hides the activity indicator.
Works great for my case. no interface hanging. and clear for the user about what's happening where and when.
Thanks to everyone!
p.s. this works for me because that's the end of the lifecycle for the retrieved data. if there were other functions depending on it - this wouldn't work:(
What about setting the object on the segue's destination View controller and then fetch the object, and then reload the view?
This would be:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "1") {
var svc = segue.destinationViewController as ViewControllerQuotes;
svc.yourObjectWhichIsToFetch = yourObjectWhichYouWantToPass //need to create a variable on the ViewControllerQuotes!
}
}
And then in the ViewControllerQuotes class:
class ViewControllerQuotes:ViewController {
var yourObjectWhichIsToFetch:PFObject?
override func viewDidLoad(){
super.viewDidLoad()
yourObjectWhichIsToFetch.fetchInBackgroundWithBlock({
(object, error) -> Void in
if error == nil {
//update the UI
}
})
}
}
With this code, you don't need to show any loading note (what is really ugly).

Resources