Open new ViewController from itself - swift

I have a view controller where I present a multiple choice question to the user. The idea is to skip to next question, so I would like to call the same viewcontroller (newsViewController) again to each new question just changing the variable question. I have a button btnNetQuestionAction. I tried this solution, and it works fine. is this a good approach ? Any better idea ?
#IBAction func btnNetQuestionAction(_ sender: Any) {
question = question + 1
if question > total {
question = total
}
UserDefaults.standard.set(questao,forKey: "questao")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "newsViewController") as UIViewController
navigationController?.pushViewController(vc,animated: true)
}

There is no silver-bullet rule that judges whether it's a good approach or not. It's a valid code anyway, it depends on how it should behave.
Based on the current approach, a stand-alone view controller will be created for each question thus added to the navigation stack; It's a good idea if you believe that each view controller should have it own UI behavior (for example), it will be easy to maintain since they are separated.
Another option is to keep dealing with the same view controller by changing the question and reload the UI. Again I wouldn't confirm it's a better approach, but at least it's an available one.

I don't think this is a great idea. You are piling UIViewControllers one on top of the other. A much cleaner solution would be to have a data source for your viewController. Have the button message the data source, update the data, and redisplay your viewController.

Related

Black screen with viewDidLoad after if else statement

I want my app to check at start conditionaly if a variable is correct or not. Based on that it should either go to an intro screen (where he can select a variable in my case select a team) or it should start the main view. After searching i found this code and edited it. But there still seems to be problems. First of all I dont have two identifier. The Intro has one but not the main view. My main View is called WeatherViewController and the Intro screen is called FirstScreenViewController. I also added a picture of my Main.storyboard.
I also googled a lot about conditional UINavigationController but I can only understand with a video and did not found a video about it.
I tried to use the code from here.
var id = hello ? "goToIntro" : "???"
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let WeatherViewController: UIViewController = mainStoryboard.FirstScreenViewController(withIdentifier: WVC has no identifier??) as UIViewController
self.window?.rootViewController = WeatherViewController
self.window?.makeKeyAndVisible()
if hello {
self.performSegue(withIdentifier: "goToIntro", sender: self)
} else {
/here nothing should happen. It should open the Main View
self.performSegue(withIdentifier: "???", sender: self)
}
Note: This answer was referring to the original question (before any edits) that was attempting a segue inside loadView().
You're supposed to manually create your controller's view in loadView. You're currently doing nothing, hence the black screen. Furthermore, if you are using a Storyboard or a xib for managing this controller you shouldn't be overriding loadView at all.
Having said that it might be a better idea to move this branching logic a step back, to "something" (a container controller like a UINavigationController or a custom one, or even directly setting the root controller of your window if it makes sense) that will present (or set) A or B based on some condition and thus avoid to load this controller altogether (have in mind that in your code, the controller is initialized, it will be part of the hierarchy and all the lifecycle methods will be called even if you segued directly to another one)
Finally, if you decide for some reason to override loadView you don't have to call viewDidLoad yourself, the system will call this method.

NSView not being deallocated

I've got a weird situation here that's almost certainly because I'm new to macOS development and I am missing some core knowledge.
I have a modal sheet that I'm displaying programmatically. (I'm not using a storyboard segue because it needs to be the result of a validation and so far I haven't seen a way to launch a segue programmatically - that's a sub-question here if anyone has advice)
Here's how I'm doing it:
searchVC = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "SearchSceneIdentifier") as? SearchViewController
if searchVC != nil {
searchVC!.searchTerm = searchTextField.stringValue
self.presentAsSheet(searchVC!)
}
This presents the sheet nicely and lets me interact with it. In it, I'm using a class which has a delegate in order to return asynchronous search queries.
Where this gets weird is that when I call
self.view.window!.close()
from inside the view controller, I don't think the view controller is getting deallocated. This seems to be because the delegate is still connected to it, even though the object that has this delegate is within the scope of the view controller itself. This delegate appears to be holding the view controller in memory.
I've gotten around this by doing this before closing the window:
search.delegate = nil
But this is not a good solution for other view controllers that have the same problem because they are inside windows and I don't want to have to catch the window closing then send some kind of notification to each in order to nil-ify their delegates.
Another approach that seems wrong as well is that I keep a reference to these windows in the application delegate and nil-ify it from there.
All of these seem like nasty solutions to the deallocation problem and my hope is that there is a cleaner way of doing this. In Objective-C, reference counts were always a problem but there were patterns to handle them cleanly.
Any advice appreciated.
I updated all my delegates to weak var and that has solved all of my problems with deallocation.

How do I remove a view controller from the memory?

I am using segues to present a new view controller. I have a 1 button that goes to VC 2. I am using this code:
func jumpToVC() {
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.performSegueWithIdentifier("whatVC", sender: self)
}
}
However the VC will not remove itself from the memory. How can I achieve this? I am presenting my views modally.
I think that you got a little bit lost here. What you want is a segue that will cause you to change UIViewController object to another one. You don't need to delete the first UIViewController and it's against practice in Swift.
From what I guess, you're just starting your journey with Swift and I know how confusing it might be. Therefore, I recommend you try this tutorial: https://itunes.apple.com/us/course/developing-ios-9-apps-swift/id1104579961
I've recently learnt from that and it's the best thing I've found.
Let me know if you have any more questions and whether I can help you somehow.

swift - call code from another view controller

I'm using a library that manages the swiping between view controllers.
It seems like the only way that I can use a button in one of my view controller to navigate to the other is by calling a specific method from the view controller that manages the swiping feature.
It there any way to call a method in a different view controller?
Thank you
There are many ways to communicate between view controllers. One of the most common patterns for doing so on iOS is the delegate pattern. One view controller holds a reference to the other and the delegate conforms to a protocol. The protocol is a set of methods that the first view controller can call when specific events happen. I would try setting up a protocol and have one of your view controllers be a delegate to the other
If you take a look at the code that is hosted on GitHub you can see that the page controller is exposed from the EZSwipeController class. So given that you subclass EZSwipeController (or maintain a reference somewhere), you can now access the pageViewController property of it and scroll to a given page.
If you subclass:
class MyVC : EZSwipeController{
func buttonTapped(){ /* use self.pageViewController to "page" */ }
}
Weirdly, I have never personally worked with UIPageViewController, and as far as I can tell there is no easy way to scroll to a page in an easy fashion.
I haven't personally tried it (I usually validate my answers before posting), but you should be able to pull it off. If you don't have a reference to the view controller you need to get it (let me know if that is the core issue).
There is a question on SO that seems to enjoy some popularity regarding UIPageViewController paging:
UIPageViewController, how do I correctly jump to a specific page without messing up the order specified by the data source?
And from Apple docs:
I would also encourage you to look at the code inside of the EZSwipeController repo, where you can find a non-exposed method that does the scrolling:
#objc private func clickedLeftButton() {
let currentIndex = stackPageVC.indexOf(currentStackVC)!
datasource?.clickedLeftButtonFromPageIndex?(currentIndex)
let shouldDisableSwipe = datasource?.disableSwipingForLeftButtonAtPageIndex?(currentIndex) ?? false
if shouldDisableSwipe {
return
}
if currentStackVC == stackPageVC.first {
return
}
currentStackVC = stackPageVC[currentIndex - 1]
pageViewController.setViewControllers([currentStackVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
}
The "key" line is this one:
pageViewController.setViewControllers([currentStackVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
I think EZSwipeController also exposes the view controllers contained in the pageViewController which is a property called stackVC, which is an array of the view controllers contained in the page view controller.
I assume with this knowledge you should be able to page to a given page, despite seeming a little "hacky" (IMHO the developers should have exposed paging logic from the get-go).
My advice for you after all this is simple:
Try to roll it yourself. This is not a huge undertaking and you maintain full control over what's accessible and how you want to work with it. The EZSwipeController class is quite small, so there is not a whole lot of code you'd have to write for your own solution. You could also go ahead and fork the repo and modify/use it to your liking.
I hope that helps. If you have any further questions or if something is unclear, I'd be ready to help you out.

Come back to the tabBarController, swift

Currently on my viewController : Upload, my button send the data to my database only if all the information are filled out, and I come back to the preview view (table View) with :
self.navigationController?.popViewControllerAnimated(true)
I would like, if it is possible, to come back to my main view on the tabBarController. I tried many things, like directly on the storyboard with Present modally segue to "TabBar controller", but I come back to the TabBar without sending my data to the database and without checking in the information are filled out..
How can I do it?
Thanks!
UITabBarController has a property selectedIndex with which you can switch the selected tab. So on completion after dismissing the UploadViewController you can run:
self.tabBarController?.selectedIndex = 0 // Index to select
It would probably be best to create a delegate for your UploadViewController to fire a function to do all the work in your previewVC on API call completion.
(Super late response...in case someone has similar questions, presumably in later version of Swift, such as mine which is Swift 5, iOS 13.2). Steps:
Be sure to set an id for your UITabBarController storyboard, e.g. "TabBarViewController"
Next, add the following to an action that has already been connected to a button:
let ID_TABBAR = "TabBarViewCOntroller"
#IBAction func returnToTabbar(_ sender: Any) {
let tabBarController = self.storyboard?.instantiateViewController(identifier:ID_TABBAR) as! UITabBarController
self.navigationController?.pushViewController(tabBarController, animated:true)
}
Referenced from one of the responses from this post.
Update: In case your Tab Bar View Controller also happens to be the root view controller, the two lines of the code in the returnToTabbar method above can be:
self.dismiss(animated:true, completion:nil);
self.navigationController?.popViewController(animated:true);
(ref.: See answer here, for Swift4 but works just fine in Swift5)