how to go back root view controller from recursive view controller(fourth) - ios

class FourthViewController : UIViewController {
#IBOutlet weak var previousLabel: UILabel!
#IBOutlet weak var backButton: UIButton!
var delegate: FourthToFirst?
var label = ""
// MARK: - Lifecycle method
override func viewDidLoad() {
super.viewDidLoad()
previousLabel.text = label
let fourthViewController = storyboard?.instantiateViewController(withIdentifier: "FourthViewController") as? FourthViewController
navigationController?.pushViewController(fourthViewController!, animated: true)
}
// MARK: - IBAction
#IBAction func backToFirst(_ sender: Any) {
navigationController?.popToRootViewController(animated: true)
}
Actually I am in fourthviewController it was going recursively(i.e pushing the fourthViewcontroller again and again, nonstop)
if I pressed back button in the controller I have to go back(i.e firstviewcontroller)
problem is:
In my code it was going (i.e non stop)
I can't press the back button to go back(i.e firstviewcontroller)

navigationController?.popViewController(animated: true) instead of navigationController?.popToRootViewController(animated: true) could be what's you need mate. (Based on what's you just edited)

Related

Loading a UIViewController into a Container View programmatically

This is in the latest XCode, 11.3. I've looked through the answers to similar questions, but some of the function names and parameters seem to have changed.
While working through a Udemy course, I'm trying to make a simple log in / sign up interface. In my first view controller I have a vertical stack view containing a label, a segmented control, and a container view. The segmented control can be either "Log In" or "Sign Up". I want to load the appropriate view controller when the segmented control changes.
My LogInViewController has a vertical stack view with a text field for username, another for password, and a Log In button. My SignUpViewController has a vertical stack view with text fields for email address, username, password, and password confirmation followed by a Sign Up button.
My first view controller looks like this:
class ViewController: UIViewController
{
#IBOutlet weak var logInSignUpControl: UISegmentedControl!
#IBOutlet var customContainer: UIView!
var logInVC: LogInViewController?
var signUpVC: SignUpViewController?
var activeVC = 0
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
initializeCustomControllers()
}
func initializeCustomControllers()
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
logInVC = storyboard.instantiateViewController(withIdentifier: "LogInViewController") as? LogInViewController
signUpVC = storyboard.instantiateViewController(withIdentifier: "SignUpViewController") as? SignUpViewController
logInVC?.willMove(toParent: self)
logInVC?.view.frame = customContainer.bounds
customContainer.addSubview(logInVC!.view)
addChild(logInVC!)
logInVC?.didMove(toParent: self)
}
Unfortunately, this only shows the LogInViewController, not the label or segmented control.
I haven't been able to find a clear description of how to do this in the latest version of XCode and Swift, so I've pieced this together from various blog posts and Stackoverflow comments about older versions. Any help is greatly appreciated.
Try the below code :)
PS: #IBAction func controlChanged(_ sender: Any) is an IBAction for the "ValueChanged" Event
class ViewController: UIViewController {
#IBOutlet weak var logInSignUpControl: UISegmentedControl!
#IBOutlet var customContainer: UIView!
var logInVC: LogInViewController?
var signUpVC: SignUpViewController?
var activeVC = 0
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
initializeCustomControllers()
}
#IBAction func controlChanged(_ sender: Any) {
changeVC()
}
func changeVC() {
if activeVC == 0 {
signUpVC?.view.removeFromSuperview()
logInVC?.willMove(toParent: self)
logInVC?.view.frame = customContainer.bounds
customContainer.addSubview(logInVC!.view)
addChild(logInVC!)
logInVC?.didMove(toParent: self)
} else {
logInVC?.view.removeFromSuperview()
signUpVC?.willMove(toParent: self)
signUpVC?.view.frame = customContainer.bounds
customContainer.addSubview(signUpVC!.view)
addChild(signUpVC!)
signUpVC?.didMove(toParent: self)
}
activeVC = activeVC == 0 ? 1 : 0
}
func initializeCustomControllers()
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
logInVC = storyboard.instantiateViewController(withIdentifier: "LogInViewController") as? LogInViewController
signUpVC = storyboard.instantiateViewController(withIdentifier: "SignUpViewController") as? SignUpViewController
changeVC()
}
}
Make sure this #IBOutlet var customContainer: UIView! is linked to the containerview and not to the vc's main view in storyboard

ViewController object is nil

I have 2 view controller.
1. ViewController (VC)
2. WebViewController (WVC)
In VC I click on a button and WVC is shown.
After successfully completing all tasks in WVC, I want to show VC and execute a particular function in VC.
Now, Storyboard contains some objects and add to VC using click and drag.
Initially, it has some values(not nil).
I Click on button, WVC is pushed, After completing all task in WVC,
VC is pushed.
Now some values in VC is nil.
error,welcome (UILabel, UIView) is nil...
And also is want to call a function in VC.
How it is possible.?
Code
ViewController.swift
class LoginController:UIViewController,UITextFieldDelegate{
#IBOutlet weak var password: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var error: UILabel!
#IBOutlet weak var welocome: UILabel!
......
#IBAction func BtnClicked(sender: AnyObject) {
let webViewCnt = self.storyboard!.instantiateViewControllerWithIdentifier("WebViewController") as UIViewController
self.navigationController?.pushViewController(webViewCnt, animated: true)
}
func doSomeProcess(){
}
}
WebViewController.swift
class WebViewController: UIViewController, UITextFieldDelegate,UIWebViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.start()
}
func start(){
---- do some process ----
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let ViewCont = self.storyboard!.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
self.navigationController?.pushViewController(ViewCont, animated: true)
ViewController().doSomeProcess() // call viewcontroller doSomeProcess() function
}
})
}
}
Simple your are pushing VC to WVC,
So logically after pushing WVC to NavigationController.
it added WVC to the top of Navigation Stack.
& it it better to pop this WVC from Navigation will automatically show VC with previous values.
use this
ViewController.swift
class LoginController:UIViewController,UITextFieldDelegate{
#IBOutlet weak var password: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var error: UILabel!
#IBOutlet weak var welocome: UILabel!
var isBackFromWVC = false
......
#IBAction func BtnClicked(sender: AnyObject) {
let webViewCnt = self.storyboard!.instantiateViewControllerWithIdentifier("WebViewController") as UIViewController
self.navigationController?.pushViewController(webViewCnt, animated: true)
isBackFromWVC = true
}
func doSomeProcess(){
}
func vieWillAppear(){
if(isBackFromWVC){
doSomeProcess()
}
}
}
WebViewController.swift
class WebViewController: UIViewController, UITextFieldDelegate,UIWebViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
self.start()
}
func start(){
---- do some process ----
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationController?.popViewController(animated:true)
}
})
}
}
Try to pop:
In this start() method just replace a line self.navigationController?.popViewController(animated: true)
instead of pushing the ViewController.
func start(){
---- do some process ----
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let ViewCont = self.storyboard!.instantiateViewControllerWithIdentifier("LoginController") as UIViewController
self.navigationController?.pushViewController(ViewCont, animated: true)
ViewController().doSomeProcess() // call viewcontroller doSomeProcess() function
}
})
}
Reason:
When you are pushing to any ViewController, The storyboard will create a new reference of ViewController and the OOP's says every object has its own properties and method so that's why your pushing view controller properties may be nil assume that your username: UITextField! text will be nil due to separate instance of ViewController.
For calling the function of your view-controller you have to implement the delegate if it is not for every time else you may work in viewwillappear.
From my understanding, probably what you want is to dismiss WVC and go back to VC.
in WebViewController's start():
self.navigationController?.popViewController(animated: true)
and after pop, you need to execute doSomeProcess() function. There're several ways to do this and I'll introduce one here.
in ViewController, implement UINavigationControllerDelegate:
func navigationController(_ navigationController: UINavigationController, animationCOntrollerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewCOntroller) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .pop:
let to = toVC as! ViewController
to.doSomeProcess()
default:
break
}
return nil
}
of course you'll need
self.navigationController?.delegate = self
inside VC's viewDidLoad() as well.

Segue from bar button item in navigation bar to another view controller won't run my code?

I'm having problems with my segues in my app.
I added a bar button item to my navigation bar and ctrl dragged it to another view controller which works as expected when tested. I ctrl dragged it again this time making it an Action in its own VC and added code.
What I want to happen when it is pressed as well as performing the segue but only the segue happens. If I delete the segue then the code runs but I can't get both to work together like I was expecting.
Here is my code:
import UIKit
import CoreData
class NewClientViewController: UIViewController, UITextFieldDelegate {
var managedObjectContext: NSManagedObjectContext!
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var ageTextField: UITextField!
#IBOutlet weak var telephoneTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var heightTextField: UITextField!
#IBOutlet weak var weightTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
}
// Dismiss keyboard when empty space tapped
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
nameTextField.endEditing(true)
ageTextField.endEditing(true)
telephoneTextField.endEditing(true)
emailTextField.endEditing(true)
heightTextField.endEditing(true)
weightTextField.endEditing(true)
}
// Dismiss keyboard when return tapped
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#IBAction func saveButton(_ sender: Any) {
let clientItem = Client(context: managedObjectContext)
clientItem.name = nameTextField.text
clientItem.age = ageTextField.text
clientItem.telephone = telephoneTextField.text
clientItem.email = emailTextField.text
clientItem.height = heightTextField.text
clientItem.weight = weightTextField.text
do
{
try self.managedObjectContext.save()
print("Successfully Saved!")
}
catch
{
print("Could not save data \(error.localizedDescription)")
}
}
}
Drag a segue from your firstViewController to your secondViewController instead and add an identifier to it instead. And then in your button action you just do this:
performSegue(withIdentifier: "yourIdentifier", sender: nil)
Add the below function if you want to pass any values:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourIdentifier" {
let secondViewController = segue.destination as! SecondViewController
}
}
I was still having trouble and managed to fix it using the
performSegue(withIdentifier: "yourIdentifier", sender: nil)
But it had to be from the VC i was wanting to come from as i'd have expected.
The issue that was stopping this happening properly was the button. When i created it up in the nav bar i wouldn't get the touch up inside so to overcome this i made the button and just placed it on the VC not it's nav bar, I linked it up as normal and added my code then dragged it into the nav bar and everything worked!

Swift Delegate function not being executed

I have a UIPageViewcontroller containing three ViewControllers. One Viewcontroller is my ProfileViewcontroller. I have a button in my ProfileViewController, which should tell the UIPageViewCongtroller when pressed, to switch to the next Viewcontroller.
It's my first time implementing a delegate, but I just can't figure out why its not working.
UIPageViewController class:
class PageViewController: UIPageViewController, ProfileViewControllerDelegate {
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
self.newColoredViewController("Profile"),
self.newColoredViewController("Offer")]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if orderedViewControllers.count >= 2 {
scrollToViewController(orderedViewControllers[1])
}
}
// MARK: ProfileViewControllerDelegate
func profileViewControllerDidTouchOffer(viewController:ProfileViewController, sender: AnyObject) {
scrollToNextViewController()
print("I'm pressing the offer Button")
}
ProfileViewController class:
protocol ProfileViewControllerDelegate : class {
func profileViewControllerDidTouchOffer(controller: ProfileViewController, sender: AnyObject)
}
class ProfileViewController: UIViewController {
weak var delegate: ProfileViewControllerDelegate?
#IBOutlet var profileImageView: SpringImageView!
#IBOutlet var offerButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func offerButtonTouchUpInside(sender: AnyObject) {
delegate?.profileViewControllerDidTouchOffer(self, sender: sender)
}
Answer
I updated the PageViewController class by changing the way I add the ViewControllers in orderderedViewControllers:
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
let profileVC = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("ProfileViewController") as! ProfileViewController
profileVC.delegate = self
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
profileVC,
self.newColoredViewController("Offer")]
}()
Looks like you're using the InterfaceBuilder so I'm not sure how it's done there (probably in prepareForSegue), but a typical pattern is something like this:
let vc = ProfileViewController()
vc.delegate = self // Or whatever you want to act as the delegate
// Now show the view controller.
The key is that before using the view controller, you make sure it's delegate property is set to the thing that is the delegate and conforms to the protocol. Typically the calling controller would be the delegate.
EDIT: If you're using the UIPageViewController, then you should add the delegate to the viewController before passing it to setViewControllers https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/#//apple_ref/occ/instm/UIPageViewController/setViewControllers:direction:animated:completion:

Custom View Label Won't Change

My view contain a button which bring a custom view which contain a ContainerView and it has a label. When I bring the custom view to the front I get the label text empty. So please where would be my issue?
View Controller Code:
#IBAction func PushAlert(sender: UIButton) {
let alert = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("alertViewController") as! AlertViewController
var alertText = AlertTextViewController()
alertText.lblText = "DONE"
alert.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
alert.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
self.presentViewController(alert, animated: true, completion: nil)
}
class AlertTextViewController: UIViewController {
#IBOutlet weak var lblAlertText: UILabel!
var lblText = ""
override func viewDidLoad() {
super.viewDidLoad()
lblAlertText.text = lblText
}
}
I have created a link for the source code which will be much easier to understand source code
What about var lblText: String? in your AlertTextViewController.
Maybe it's overriden by your declaration (for some reason I wouldn't be able to explain)?
Or wait, I'm confused by
var alertText = AlertTextViewController()
as you are presenting creating and presenting the ViewController alert. How is alertText and alert related?
Edit:
class AlertViewController: UIViewController {
#IBOutlet weak var container: UIView!
override func viewDidLoad() {
super.viewDidLoad()
var alertText: AlertTextViewController = self.childViewControllers[0] as! AlertTextViewController
alertText.lblAlertText.text = "Test"
// Do any additional setup after loading the view.
}
Do this in your AlertViewController, and it works. I don't know what your final purpose is though, and this might not be the most elegant solution. I've created an outlet, the containerView to your class.
I think you may have a order of events problem. Try setting lblAlertText.text in viewWillAppear instead of viewDidLoad.

Resources