Open same View Controller - ios

Using Xcode 9 / Swift 4, I have a tile application (same as Windows 10 presentation).
When user is on main view controller and click on a tile, I want to open the same view controller and set parentId, in order to show child tiles.
App can have infinite number of child of child.
But, app crash when I try to push same View Controller as current displayed:
let vc = currentStoryboard.instantiateViewController(withIdentifier: "TileViewController") as! TileViewController
vc.parentId = id
vc.title = item.name
currentViewController.navigationController?.pushViewController(vc, animated:true)

The exception your are having its not related of pushing the same instance another time. Well you are probably adding some constraints programmatically, And also its being added in viewDidLoad/viewWillAppear/viewDidAppear because thats what can i understand from the exception that your getting:
Impossible to set up layout with view hierarchy unprepared for constraint.
If its so then you'll need to add it in either updateViewConstraints() which is called when the view controller's view needs to update its constraints, or inside viewWillLayoutSubviews().

Related

How to make navigation after Splash Screen

I'm new to iOS. I have a storyboard with a navigation controller as initial view controller:
And I need some splash screen as a first screen after Launch screen to determine if user has a subscription or not. Depending on the result, I want to get to navigation view controller which is now initial view controller or to subscription screen (which is now the third in the tree).
In the second case, I want to save the existing structure where the navigation controller remains at the top
I found a couple of manuals, but they did not work in my case. How to do it properly?
tell me if i have not indersttod well. #Victor Pro his solution inspired me.
//1.
You need a like SplashScreenVC after launch screen. so in storyboard you can set it as a initial view controller.
//2. After determining in any case
Preset a Initial Navigation Controller which is your main controller.
subscriptionNeeded = true , if subcription needed
///3. If your subscription needed then
take one flag in appdelegate/global variable that subscriptionNeeded , and set it true.
///3.1 when you do point 2, in that in navigation controller -> root view controller -> View Will Appear
if subscriptionNeeded {
subscriptionNeeded = false
Push / Preset a sunscription VC
}
Tell me if i am not getting. Or u not get what i wrote.

ViewController Modal Presentation not appearing

I have a view controller that is a child view controller of my window's root view controller. That child view controller has a table view and when i select a row it tells the parent view controller to present a view controller modally. The modal view controller, however, never appears. I created a bare bones test view controller that just prints viewDidLoad and viewWillAppear. What I notice is that when I call parentVC.present(testVC, animated:true, completion:nil), viewDidLoad is run, but viewWillAppear is not. viewWillAppear is only then called when I interact with the UI in some way. Whether tapping, panning, scrolling or whatever.
I've spent hours trying to debug this. It doesn't seem like the main queue is blocked and I've reduced the problem to its bare bones. The modally presented view controller's viewWillAppear is simply not called until I interact with the UI again.
What could be causing this symptom?
In comments, you mention that you're instantiating your view controller with
let vc = TestVC()
where TestVC is presumably a (largely empty) UIViewController subclass.
A view controller needs a view created either via either storyboard scene (using instantiateViewController), a NIB or, in very rare cases, a view you create in loadView (which you shouldn’t be confused with viewDidLoad).
I’d suggest creating a storyboard scene (assuming you are using storyboards), give it a storyboard ID, and then use instantiateViewController:
let vc = storyboard.instantiateViewController(withIdentifier: "foo")
But just having a UIViewController subclass called TestVC and instantiating it with TestVC() won’t work.
In our discussion, you said you wanted to do this programmatically with no NIB nor storyboard. If so, use loadView. See https://stackoverflow.com/a/37964249/1271826 for an example.

Detached Controller Issues on a Dynamically Loaded UIStackView

I am getting a "Presenting view controllers on detached view controllers is discouraged" warning in a somewhat specialized architecture. And - there are some fairly big UI issues resulting from it. I have an architecture with 2 distinct unconnected groups in my storyboard. The first group is the main interface of my app and includes an UIStackView. The second group consists of an UIView plus attached popover segue as shown in the image below.
I dynamically populate the UIStackView of group 1 with up to 8 instances of the UIView of group 2. This is done in a function called loadViews() in the UIStackView subclass which is called as needed. Here is the cleaned up pseudo code for illustration:
for i in 0 ..< green.count {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let greenVC = storyboard.instantiateViewControllerWithIdentifier("greenViewController") as! GreenVC
greenVC.progressionStackView = self
greenVC.index = i;
greenViewControllers.append(greenVC)
if let greenView = greenVC.view as! GreenView! {
greenView.fillColor = UIColor.orangeColor()
greenView.setNeedsDisplay()
self.addArrangedSubview(greenView)
}
}
self.layoutIfNeeded()
Every time I trigger the popover on one of the embedded green views in the stack view I get the warning from above. More importantly, when running on an iPad in split view mode, the stack view loses a green view each time until there are none left. The latter is just a display issue, because on refresh all views are back.
I am completely stumped and am not sure how to fix this or implement things differently. If the issue is that the loaded views are not attached, can they be "re-attached"? Or is there a way to dynamically load a stack view with up to 256 views that are attached to it?
Solved:
Another lesson in taking Xcode warnings to heart - even if the word discouraged is used. As in this case, things tend to break. The solution was obvious in hindsight. The new view controllers that were instantiated as greenVC had to be attached to the containing view controller - i.e. the one several layers up in the view hierarchy that contains this UIStackView:
vcContainingStackView.addChildViewController(greenVC)
The line above is called right before appending greenVC to my array of added view controllers. Of course now removeFromParentViewController() has to be called as well where instances of GreenVC get removed, but otherwise that's it. The warning is gone and so is the issue of vanishing views.

XCTest Reference to View Controller not Assessable via RootViewController

I have a application with UIViewController "hierarchy" of this form:
Tab Bar Controller
- View Controller 1
- View Controller 2
- View Controller 3
* View Controller 4
I am using the XCTest Framework to write test methods for my iOS application. View controller 3 is a UITableViewController. When the user selects a row in the table, view controller 4 is shown.
I make a call to the application's window to get the root view controller, the tab bar controller for the application:
// get reference to the tab bar controller
let tabBarController = UIApplication.sharedApplication().delegate!.window!!.rootViewController! as! UITabBarController
But that only gets me access to the three view controllers corresponding to each tab. I checked the number of child view controllers for the root view controller and the number is 3. Makes sense. I checked the number of child view controllers for the 3rd view controller...zero. The view controllers are all setup via IB. I still need to somehow get access to the 4th view controller for my test. Is there a programmatic way to reach the 4th view controller?
A good night's grinding got me to the answer:
In my test code, I was making a call on the UITableView using this:
selectRowAtIndexPath:animated:scrollPosition:
But that method call does not trigger the UITableViewSelectionDidChangeNotification notification to get posted. Since my application's code does use the UITableView delegate function that depends on that notification's posting, I instead just did this:
// get reference to the table view
let tableView = tableViewController.view.viewWithTag(300)! as! UITableView
tableView.delegate!.tableView!(tableView, didSelectRowAtIndexPath: indexPath)
Doing the row selection this way triggered my table view's delegate method to be called and thus the
performSegueWithIdentifier:sender:
inside of that method was called.
The view controller responsible for displaying the site details was loaded and I was able to get a reference to it using the presenting table view controller's method:
let detailViewController = tableViewController.presentedViewController!
This seems to work and I am satisfied thus far...

How can I keep a view controller in memory after its popped from the stack?

This is an embarrassingly simple question, but I need to preserve a view controller and my current solution is not a solution.
I have a slide out table view menu that lets me select a new view controller to push to the forefront. When I select a view, it deallocs the old main view controller to push the new one. Since 90% of my functionality revolves around one view controller, I want it to stay in memory so I don't have to constantly spend resources to allocate it and either pull the last data source from core data or make a network request.
I naively tried to set a placeholder temporaryMainViewController and assign it to my center view controller before it is assigned another VC, but assigning the current main view controller to the temporaryMainViewController simply assigns the address of the main view controller- so when it's changed, so is my tempVC.
Trying to copy the view controller causes a crash.
So how can I effectively do self.temporaryMainViewController = self.currentCenterViewController; where the temp controller is assigned by value of object and not value of address?
-- EDIT --
More info:
ECSlidingViewController keeps (in my case) 3 controllers in memory- the top/center/main view controller, a left hidden controller, and a right hidden controller. My left hidden controller is a tableview, LeftMenuTableViewController , where each row, when selected, observes the view controller class I've associated with that indexPath then instantiates an instance of that class and sets it to the topViewController with a simple assignment statement. I want to keep only my initial top view controller (of class PlacesNavigationViewController (which holds PlacesTableViewController)) in memory when a new view controller is assigned to the top view.
My first approach was to declare a placeholder property in LeftMenuTableViewController, since it never leaves memory itself.
#property (nonatomic, strong) UIViewController* temporaryViewController;
then in didSelectRowAtIndexPath:
// make local variables for storyboard and the identifier of the view controller that will be pushed, then..
self.temporaryViewController = self.topViewController;
self.topViewController = [storyboard instantiateViewControllerWithIdentifier:newViewControllerIdentifier];
but this fails because the temporary controller is assigned the memory address of the topViewController- which in the next line is given a new view controller to hold.
So what I need is a way to hold the contents of topViewController so that when topViewController changes, I still have the old VC in memory.
I'm likely forgetting some obvious tenet of Objective-C, but this is giving a good bit of trouble. Let me know if I did not make something clear.
You want to use viewControllers property of UINavigationController class. The documentation is here. Basically, you can just swap 2 view controllers in viewControllers array and then use setViewControllers:animated: to make the transition.
Hope this helps!
Cheers!

Resources