Switching between UIViewControllers using UINavigationController - xcode

There is section in my app which has a popover in which a UITableView is presented each time the popover is presented. It's easy to navigate between the UITableViews calculating the paths of the files that are supposed to be in the next view controller and using pushViewController, as easy at it is also to popViewController when it is necessary to go back to the former UITableViews.
The problem becomes when the popover is dismissed to do some other tasks and, after that, it is necessary to get back to the popover.
Which are the best practices to get a structure capable to retain the last presented UITableView with its properties and its former UITableView so the popover is presented exactly the way it was when dismissed?

It's a basic concept issue. I hope it helps somebody else.
I was initializing the UINavigationController each time the popover was dismissed. Of course, the information I needed from the "current table view" was lost. I just turned the UINavigationController from local variable into property and now the popover is every time shown with the "current" UITableView.

Related

Disabling UIBarButtonItems in Nav Bar and Stopping Secondary Popvers

The issue is as follows: I have an iOS 9 app (Swift) with a nav bar that houses a bunch of UIBarButtonItems with segues to several UINavigationControllers that finally display tables, views etc. as popovers. Of course tapping one of these buttons does not automatically disable the other UIBarButtonItems in the nav bar and the user is free to tap them. With a popover already displaying however we get the following warning as this is against the HIG and also grounds for rejection:
Attempt to present UINavigationControllerViewController which is already presenting (null)
To take care of the issue once a popover is displaying a) the remaining UIBarButtonItems need to be disabled and b) displaying additional popovers needs to be halted.
So what is the best cause of action here? The isBeingPresented flag of the popover's view controller is not available in the governing view controller because one can't create outlets of one view controller in another and the navigationController's viewControllers array doesn't include external navigation controllers or their descendants.
My lame workaround involves a global appIsPresentingPopover Bool which is set to true in viewDidLoad and to false in viewDidDisappear in the mentioned view controllers. That Bool is then tested in the governing view controller's prepareForSegue method to stop additional popovers from displaying. It is also kvo'ed to disable any enabled UIBarButtonItems which now all have an outlet. Ugly!
What is the standard way of handling this common scenario?
Surprisingly simple solution and much more elegant than my initial attempt. I added segue identifiers for all involved popups and a UIViewController class variable. In prepareForSegue I set the variable to the destination view controller. Then in shouldPerformSegueWithIdentifier I simply check whether that popover variable is nil. If it is not, I dismiss the UIViewController and set it to nil. Finally I also followed Beau Nouvelle's suggestion and kept those buttons lit so users can switch directly between popovers. Thanks again.

How can I hook up NSWindow's initialFirstResponder when using Storyboards in InterfaceBuilder?

I'm frustrated with the wall that using storyboards seems to put between NSWindows/NSWindowControllers and NSViews/NSViewControllers. A specific example: What good is the initialFirstResponder outlet on NSWindow if the whole view hierarchy is in a separate scene and can't be referenced?
I'd like to make the window called "progred"'s initialFirstResponder the "Content View" view from the View Controller Scene, but you can't make IBOutlet references across scenes. I'd be perfectly happy to put the ViewController and the Window in the same scene, but I can't seem to get that to work with all of the dragging/dropping I've tried to do from one scene to another, or even trying to add a new one from the toolbox. It seems that you're required to use a segue relationship to assign a NSViewController to a NSWindow.
I've googled but I'm not finding anything on this problem which might just mean I'm missing something obvious as I'm pretty new to Storyboards as I can't imagine I'm the first to notice this :).
I've been going through just this in swift (Xcode 6.3.1). IB appears to be unable to do what we want despite all the doco and advice saying "control drag....". My (eventual) solution/workaround was to set it in code, in viewWillAppear, of the view controller. I'd tried a variety of approaches until I encounter some apple doco that declared that the initialFirstResponder must be set BEFORE the window displayed. Hope this helps.
By the way, watch out for the default Window behaviour being "restorable" which seems to restart the (rebuilt) app with the focus in the last field the app was killed in....sigh

How does one display a new view controller in the same Mac window?

I'm fairly new to Mac development and am slightly confused by the new "storyboard" feature in Xcode 6. What I'm trying to do is segue from one view controller to another in the same window. As of right now, all the different NSViewControllerSegues present the view controller in a new window, be it a modal or just another window. What I'd like to do is just segue within the same window, much in the same way one would on iOS (though an animated transition is not crucial). How would this be achieved?
If you provide a custom segue (subclass of NSStoryboardSegue) you can get the result you are after. There are a few gotchas with this approach though:
the custom segue will use presentViewController:animator so you will need to provide an animator object
because the presented view is not backed by a separate Window object, you may need to provide it with a custom NSView just to catch out mouse events that you don't want to propagate to the underlying NSViewController's view
there's also a Swift-only glitch regarding the custom segue's identifier property you need to watch out for.
As there doesn't seem to be much documentation about this I have made a small demo project with custom segue examples in Swift and Objective-C.
I also have provided some more detail in answer to this question.
(Reviving this as it comes up as first relevant result on Google and I had the same problem but decided against a custom segue)
While custom segues work (at least, the code given in foundry's answer worked under Swift 3; it needs updating for Swift 4), the sheer amount of work involved in writing a custom animator suggests to me that their main use case is custom animations.
The simple solution to changing the content of a window is to create an NSWindowController for your window, and to set its contentViewController to the desired viewController. This is particularly useful if you are following the typical pattern of storyboards and instantiate a new ViewController instance every time you switch.
However.
The NSStoryboard documentation says, quite clearly in macOS, containment (rather than transition) is the more common notion for storyboards which led me to look again at the available tools.
You could use a container view for this task, which adds a NWViewController layer instead of the NSWindowController outlined above. The solution I've gone with is to use an NSTabViewController. In the attributes inspector, set the style to 'unspecified', then select the TabView and set its style to 'tabless'.
To change tabs programatically, you set the selectedTabViewItemIndexof your TabViewController.
This solution reuses the same instance of the ViewControllers for the tab content, so that any data entered in text fields is preserved when the user switches to the other 'tab'.
Simple way with no segues involved to replace the current view controller in the same window:
if let myViewController = self.storyboard?.instantiateController(withIdentifier: "MyViewController") as? MyViewController {
self.view.window?.contentViewController = myViewController
}

Dragging UIView as subview of UITableView lags behind finger

I have designed a custom subclass of UITableView. At some point in its use, I need to add a UIView as a direct subview of UITableView, drag it within the table view using my finger, and then remove it from the table view. I use a UILongPressGestureRecognizer to create a view in much a similar manner as the iOS Calendar app, drag it around the tableview by resetting its frame, and then remove it from the table view by adding it to a cell.
This works, but with one caveat. When I move my finger too quickly, I "lose my grip" on the UIView, and it does not catchup to my finger unless I move my finger back within the frame of the view to "pick it up" again.
Since UITableView subclassed from UIScrollView, I tried setting the table view's delaysContentTouchs and canCancelContentTouches properties to NO, while setting the exclusiveTouch property of the UIView being dragged to YES. Nothing has worked.
Ultimately, I want to be able to drag my UIView around inside the table view with just as much responsiveness as the built in iOS Calendar app. Any thoughts?
P.S. I am relatively new to iOS development, so please forgive if there is some major oversight here.
Problem solved! As it turns out, there was nothing inherently wrong with the setup (adding a subview to a UITableView and dragging).
After much digging, I discovered that I had been performing a hitTest every time the view was dragged to a new coordinate. One could see how performing such an intensive operation every second could slow down processing enough on the device to cause this weird behavior. When I changed it, everything worked perfectly.
Ultimately, the lesson to be learned was that my view controller subclass was too complex and therefore not easily readable. There was too much digging necessary to uncover this issue. I have since taken great pains to simplify my code - it has been incredibly rewarding.

Switch XIB Views - Cocoa Mac

I am pretty new to coding and would like to know how to switch XIB views on a button click. IS there also anyway to add animation when switching?
Thanks,
Kevin
this is totally possible, but there are a few things you'll need to do. I imagine you are already familiar with connecting outlets to objects in your XIB, so the first thing you need to do is create the custom views in your XIB and connect them to outlets in your appDelegate. I suggest that one of the views be dragged into the window and one one be outside the window. That way, when the window loads, it already has one of your custom views as a subview. This just makes it easier to get started.
Then you're going to write an IBAction in the appDelegate and connect it to your button. Assuming that one of the custom views is already being hosted by the window, the IBAction should send a replaceSubviewWith message to the window's contentView animator like this [[window.contentView animator] replaceSubview:firstView with:secondView]; where firstView and secondView are the pointer/outlets that you declared and connected to the views in your XIB.
This is sending the animator proxy of the window's content view a message which tells it to replace the old subview with the new one. The reason for sending the message to the view's animator proxy (and not the view itself) is that the transition will be carried out with the deafult CATransitionAnimation. Because you want it to be animated, right?
The reason why you shouldn't remove one subview and then add another is because animating the removal of a subview is actually quite tricky and requires the implementation of the delegate method animationDidEnd. This is because executing an animation on a view that has been removed from the view heirarchy does not make sense. I don't know why Apple hasn't changed this, but for now it will be one of the enduring quirks of CoreAnimation.
Let me know if that helps. I am happy to clarify! And welcome to Cocoa!
An easy way to do this is to use a tabless NSTabView- you can lay everything out in IB so the pain is minimal.

Resources