Switch XIB Views - Cocoa Mac - cocoa

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.

Related

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.

Forcing Interface Builder to change class of objects(UIButton->UIControl)

I have a UIView filled with buttons that are all nicely hooked up to actions and outlets. However, in my infinite wisdom, I decided that I really would rather have the button behavior to be different and subclassed a UIControl.
My plan was to hop into Interface Builder, change the class of the buttons to my new UIControl subclass, and then be up and running. This would preserve all of outlet and action connections.
In IB (View Identity Inspector) when I type in my UIControl subclass into the class field, it reverts back to UIButton when I tab out. Any UIButton subclass works, but not a UIControl. I can go down the inheritance tree but not up....
The first plan was to go to XCode and change the superclass of my new control temporarily to UIButton, change the 'class' of my IB buttons, and then change the XCode code superclass back to UIControl. IB accepted and changed the class, but running the app gives me non-visible buttons. The IB Attributes inspector still shows it as a button.
Creating the control from scratch and rewiring works, but I was hoping to not rewire all the buttons if it could be avoided. (This is a change I was hoping to roll across multiple apps, so it is a bit more painful that it sounds)
Anyone know any way around this?
many thanks!
I'm a year late on this, but maybe it will help someone in the future. Perhaps you could open the .xib in a text editor and figure out what text you would have to change to get it to work (try it on a sample project first), then use find and replace to fix all of them at once.

Display NSWindow as Subview of NSView

I have an NSWindow that I want to display as part of another window. It has to be an NSWindow; I don't want to change it to an NSView or anything... I just don't because it involves accessing foreign nib files and the such.
How should I accomplish this if possible? I was thinking along the lines of grabbing the view of the NSWindow and sticking it as the subview of another view in my main window... but I don't think this is possible. Is it?
If you're okay with the window as-is, just add it as a child window to the window in your own app. That will handle positioning and everything, although you might have to patch up the responder chain and such to really get things working properly.

Resources