So I have a log in function that goes to parse and validates log in. Then when it returns I present a different view with the following code.
var storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil);
var vc: UIViewController = storyBoard.instantiateViewControllerWithIdentifier("ProfileView") as UIViewController
println("got here");
self.presentViewController(vc, animated: true, completion: nil);
println("got here too");
got here prints to the console got here too does not, the view did load function for ProfileView never gets called. There are no errors in the log and no warnings during compilation. the application does not crash I can keep trying to log in but it will never load the next view or print the second message.
The code did work but then I altered the ProfileView a little bit and it stops. I have reverted the view and its still broken, I have cleaned and rebuilt. Is there anything I should be looking for specifically?
Upon request here are the top lines of ProfileView.
class ProfileView: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var profileImage: UIImageView!
var profilePic: PFObject!
var imageArray: [PFObject]!
when I instantiate those objects with an init method the init method will run and I can break point it but the view will not load cause I never hit the break point on the first line of viewDidLoad.
It looks like the Storyboard Identifier is not set to ProfileView in Interface Builder. Also, in the event you want to set Profile View controller specific properties, you shouldn't cast as UIViewController instead use the class of your Profile View Controller
It might also help to have a UIStoryboard extension that loads your view controllers via helper methods e.g.
extension UIStoryboard {
class func loadProfileViewController() -> ProfileViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
return storyboard.instantiateViewControllerWithIdentifier("ProfileView") as! ProfileViewController
}
}
This way you can write let profileVC = UIStoryboard.loadProfileViewController()
Related
So all my controller's are done programmatically to avoid segues and that sort of complicated stuff.
I have a viewcontroller (Call it ProfileViewController) that downloads data from the network.
So I have a method in ProfileViewController that instantiates a single storyboard file with a static tableview with cells that have textfields in them. Here is the method:
ProfileViewController:
func userSelectedUpdateProfile() {
// Obtain reference to the only storyboard file named EditProfileSB
let storyboard = UIStoryboard(name: "EditProfileSB", bundle: nil)
// Since the Tableview is embedded in a navigation controller (with ID set to "navigationID")
if let parentNavigationController = storyboard.instantiateViewController(withIdentifier: "navigationID") as? UINavigationController {
// Now find the embedded TableViewController and access it's properties to pass to.
if let childEditController = parentNavigationController.topViewController as? EditProfileTableViewController {
// ! Error here ! Found nil when doing this.
childEditController.nameTextfield.text = "Passed this to static cell"
}
present(parentNavigationController, animated: true, completion: nil)
}
}
So the code itself is self-explanatory to what I am trying to achieve here. The TableView is embedded in a Navigation (done on storyboard with "Editor > Embed In") so on the 2nd nested if let statement I am now checking to find that Edit controller and access its properties (nameTextfield).
I get a crash when I attempt to access the nameTextField.text property. This textfield is set using storyboard. Here is that EditProfileTableViewController:
class EditProfileTableViewController: UITableViewController {
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
// Other methods ...
}
Here is the storyboard flow layout.
Am I missing something here? I keep getting a crash on childEditController.nameTextfield.text = "Passed this to static cell" on the method userSelectedUpdateProfile().
If your View Controller still not call viewDidLoad().
your textfield is not create.
#IBOutlet weak var nameTextfield: UITextField!
you can see it's attribute is weak here.
Try create a value and pass text to the value. Then in viewDidLoad(), you can set the value to your textField
I'll give an example of what I want so it's not so confusing:
Example:
Let's say that I have a map that adds every time that my user scrolls 3 annotations dynamically. Now I have a button under the map and when I press it I go to another viewController do what I want and get back to the viewController with the map, now I want to find all the annotations that my map had and not reload the view at all.
I used to use this function that I made to move between viewControllers:
func move(identifier: String , viewController : UIViewController) -> Void {
let mstoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UIViewController = mstoryboard.instantiateViewControllerWithIdentifier(identifier)
viewController.presentViewController(vc, animated: true, completion: nil)
}
I also tried this:
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("view") as? MyViewcontroller
self.presentViewController(vc!, animated: true, completion: nil)
These two when I use them the viewcontroller that appears is calling viewDidload so its like it appeared for the first time.
Another example is the tabBarViewController if you notice when you navigate through tabs nothing reloads (only function that is called is viewDidAppear )
EDIT
test file
The problem is caused by the fact that the map controller gets deallocated when navigating back to the other controller, and another one is created when you want to move again to the map screen.
What you need is to hold on onto the same controller instance, and present that one. Keeping a strong reference in the presenting controller would suffice.
class PresentingController {
// making the property lazy will result in the getter code
// being executed only when asked the first time
lazy var mapController = { () -> UIViewController in
let mstoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
return mstoryboard.instantiateViewControllerWithIdentifier("mapControllerIdentifier")
}()
func moveToMap() {
// simply use the mapController property
// the property reference will make sure the controller won't
// get deallocated, so every time you navigate to that screen
// you'll get the same controller
presentViewController(mapController, animated: true, completion: nil)
}
}
According to the same project you posted, you instantiate a new UIViewController when going from view 2 back to view 1 and that is why your viewDidLoad gets called again and your entire map view is reloaded.
In your sample project, instead of
lazy var mapController2 = { () -> UIViewController in
let mstoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
return mstoryboard.instantiateViewController(withIdentifier: "first")
}
You should just dismiss your view 2 on the button press.
#IBAction func butto(_ sender: AnyObject) {
//Your initial code
//PresentingController().moveToMap(self, flag: 1)
self.dismiss(animated: true, completion: nil)
}
When you present a new UIViewController, the older UIViewController is not removed from memory, it is just hidden behind the new UIViewController. So whenever you wish to go back to a UIViewController with the previous state maintained, all you need to do is close the new UIViewController
However, if you are doing some tasks that you performed on your second UIViewController that you wish to be reflected in your initial UIViewController, you will have to setup closures to update your initial UIViewController.
I hope someone can help me with this problem I am getting with swift.
I am trying to add an array of UIViewControllers to a UIPageViewController. However, whenever I try to access a view controller through the method instantiateViewController, I get a SIGABRT error.
let vc: UIViewController = self.storyboard?.instantiateViewController(withIdentifier: views[index]) as! UIPageViewController
Here is my entire ViewController.swift file just for the reference.
import UIKit
class ViewController: UIViewController {
var pageViewController: UIPageViewController!
var views: [String] = ["view1","view2"]
override func viewDidLoad() {
super.viewDidLoad()
self.pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "pageViewController") as! UIPageViewController
var arr: [UIViewController] = []
for i in 0..<views.count{
arr.append(assignView(index: i))
}
self.pageViewController.setViewControllers(arr, direction: .forward, animated: true, completion: nil )
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
pageViewController.didMove(toParentViewController: self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func assignView (index: Int) -> UIViewController{
let vc: UIViewController = self.storyboard?.instantiateViewController(withIdentifier: views[index]) as! UIPageViewController
return vc
}
}
Can someone tell me why my code is throwing me this error?
Thank you so much!
From the line below:
self.storyboard?.instantiateViewController(withIdentifier: "pageViewController") as! UIPageViewController
It seems that storyboard is treated as optional. It may be that the storyboard variable is getting nil when you are trying to access it.Ideally it should not be nil and should not be optional.
If the optional is purposefully done then you can apply a "if let" to check before you access.
Let me know if you need further clarification.
SIGABRT is a controlled crash and the app terminated mostly on purpose. It mostly resulted from programmer's fault.
Look for following as there is two possible causes to crash.
self.storyboard doesn't properly got initialised as there is no corresponding storyboard or incorrectly referred.
View controller's identifier is incorrectly referred.
Consider the following example
var mainView: UIStoryboard!
mainView = UIStoryboard(name: "Main", bundle: nil)
let samplecontroller : UIViewController = mainView.instantiateViewController(withIdentifier: "iPhone")
In the above example mainView is my instance referring to StoryBoard which is implicitly unwrapped(this is done on purpose to specify that this cannot be nil)
samplecontroller is the instance of the scene in the storyBoard. "iPhone" is the name of the StoryBoardID of the scene.
This snippet will ensure that the mainView will never be nil provided my storyboard is setup correctly with the Ids and names used.
After this you can still have a safety check like
if let test = mainView {
//Do your stuff
}
Let me know if this helps.
I have a ViewController: if a user is not logged in, I open modally a new controller, so that the user can login. The opening is done this way:
if(!loggedIn){
self.performSegue(withIdentifier:"loginView", sender:self)
}
After login, I want to dismiss this modal and come back to my viewcontroller: this is very easy to do, but I want another thing. I want the presenting view controller call this method before I dismiss my modal:
func goToContent(animated:Bool){
let viewController:ContentViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Content") as! ContentViewController
self.navigationController!.pushViewController(viewController, animated: animated)
}
Is this possible? From my modal I could not get a reference to the presenting viewController. That is: I tried the following, but I don't know if this is the right way to proceed:
let vc:ViewController = ViewController()
vc.goToContent(animated:false)
This works but aren't there better solutions?
[Edit to reply to a question in the comments]
I implemented a delegate this way:
in the ViewController:
class ViewController: UIViewController, LoginViewControllerDelegate {
var loginViewController:LoginViewController = LoginViewController()
override func viewDidLoad() {
super.viewDidLoad()
loginViewController.delegate = self
}
In LoginViewController file, just before the class declaration:
protocol LoginViewControllerDelegate: class {
func goToContent(animated:Bool)
}
Inside LoginViewController:
weak var delegate: LoginViewControllerDelegate?
in its view did load (just for testing: I simply put a print inside the body of goToContent):
delegate?.goToContent()
You need to create a delegate of ContentViewController and in that delegate you need to add this method goToContent.
Now when you are logged in successfully, you need to call this delegate method. It will work.
I have two classes:
class ExplorerViewController: UIViewController {
lazy var studyButton: ExploreButton = {
let button = ExploreButton()
button.setTitle("Study", forState: .Normal)
return button
}()
}
and
class ViewController: UIViewController, UISearchBarDelegate, LocateOnTheMap, GMSMapViewDelegate {
}
I'm trying to make it so that when I click the studyButton, it sends the button title to ViewController and goes to that view.
I'm not using storyboards and am having trouble with segues since every tutorial seems to give different examples that are specific to the things they've been working with and 95% of them seem to be operating with storyboard. Can someone give me a general way of how to do this?
How do I give the starting view controller an identifier because it isn't instantiated like the other controllers that I 'move' to after. How can I move from ViewController to ExplorerViewController and then move back to that same ViewController (with all changes intact).
Create an initializer for your ViewController that receives the "title" variable:
class ViewController: UIViewController, UISearchBarDelegate, LocateOnTheMap, GMSMapViewDelegate {
var btnTitle: String?
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?, btnTitle:String?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.btnTitle = btnTitle
}
}
When creating the ViewController object use this initializer.
var viewController = ViewController(nibName: "ViewController", bundle: nil, btnTitle: title
You can initialize UIViewController that you want navigate to, assign data to properties in that controller and call this method:
presentViewController(viewController, animated: true, completion: nil)
For example:
let destinationViewController = ViewController()
destinationViewController.frame = self.view.frame
destinationViewController.buttonTitle = "title"
self.presentViewController(destinationViewController, animated: true, completion: nil)
Although I would suggest you to get familiar with Storyboards and perform navigation with Segues.
Make sure of two things:-
1.) You have given your viewController an StoryBoard ID lets say "viewControllerVC_ID" in it's Identity inspector
2.) You have NavigationController Embed in to your Initial entry point View Controller
In ViewController declare a variable
var btnLabelTxt : String!
Create an #IBAction of that button in ExplorerViewController :-
#IBAction func exploreBtnAction(sender : UIButton!){
let vcScene = self.navigationController?.storyboard?.instantiateViewControllerWithIdentifier("viewControllerVC_ID") as! ViewController
vcScene.btnLabelTxt = "Study"
//or you can just access the button itself in the viewController and set the title
//By vcScene.yourBtn.setTitle("Study", forState: .Normal)
self.navigationController?.pushViewController(vcScene, animated: true)
}
please see this question How to push viewcontroller ( view controller )? for how to switch between views.
to pass data once you have reference to the new view, you can assign the data to a property of that view.