How do to manual layout in NSView? - cocoa

In UIView I can do manual layout by overriding -layoutSubviews and -sizeThatFits:. This works fine when embedded in auto layout containers, etc. What is the equivalent for NSView?
-layout and -fittingSize are the obvious choices, but the documentation suggests that those are auto layout specific.

The documentation for layout says:
"Override this method if your custom view needs to perform custom layout not expressible using the constraint-based layout system. In this case you are responsible for setting needsLayout to true when something that impacts your custom layout changes."
Sounds to me like it's what you need.

Related

How can I turn Cocoa Auto Layout off for subviews of a NSView programmatically?

I have looked at the other similar questions, but none of them addressed the question of truly programmatically bypassing Auto Layout for subviews of a NSView.
I would need to tell Auto Layout not to layout subviews of a particular NSView. I've tried overriding layout(), but this only generates a warning and the subview that has no constraints on it (I have set translatesAutoresizingMaskIntoConstraints = false on it, as I don't want any constraints to be automatically generated or otherwise) gets it's frame set to all zero's anyway [x=0, y=0, width=0, height=0]. I find it hard to believe that there is no way to tell Auto Layout not to layout a view. translatesAutoresizingMaskIntoConstraints = true is not what I am looking for, as I want to disable Auto Layout completely for all subviews of a view. I have two cases where I need this:
A note taking app where the user can create and position basic geometric shapes and text views. These are all NSView subclasses. I could of course use constraints and manipulate those but what is the point in a view that is not supposed to be adaptive in any way?
A math app where I use Auto Layout to layout mathematics on separate lines. I would want to turn Auto Layout momentarily off for the lines that are not beeing edited because as you add more lines and math objects, the autolayout calculation starts to make the app progressively more unresponsive. Since the only thing needing layout calculation is the line the user is editing, the process could be optimized by turning Auto Layout off for the other lines.
There is no way to do what you want.
Auto layout is ON or OFF for the whole window, not individual views. If any view in the window has had constraints added or is of a class whose +requiresConstraintBasedLayout method returns true, then the window uses auto layout.
If you want to use the old way of positioning a view, by setting its frame and its autoresizing mask, then you want translatesAutoresizingMaskIntoConstraints to be true. Indeed, that's the default for programmatically-created views precisely for backward compatibility for auto-layout-unaware code to operate in a window which is otherwise using auto layout. Your rationale for turning it OFF is exactly backwards.
Just leave or turn translatesAutoresizingMaskIntoConstraints on and have your code take responsibility for setting the view's frame to whatever it desires. Be sure to also set the view's autoresizingMask as desired, too, perhaps to NSViewNotSizable. You will generally need to deactivate any other constraints on the view for the duration.
If you need to do this for a whole view subhierarchy, then you will have to, well, do it — enable translatesAutoresizingMaskIntoConstraints and set the frame and autoresizingMask — for the whole subhierarchy. However, this doesn't seem like a reasonable approach to me.

Not using Auto Layout, yet see 'Unable to simultaneously satisfy constraints' error

I'm not using Auto Layout in any of my nib files. I'm loading a NSViewController and then adding its view to another NSView manually. However when I do that, I am seeing this error:
Unable to simultaneously satisfy constraints
This makes no sense to me since I don't have autolayout enabled anywhere. I have specifically disabled it. I have control over the NSViewController being loaded and the NSView I'm adding it's view to. What can I do to fix this?
Auto layout is enabled (or not) at the window level. If any view in the window has had constraints added to it or overrides +requiresConstraintBasedLayout to return YES, then auto layout is enabled for the window. Any views which are not coded to participate in auto layout would probably have translatesAutoresizingMaskIntoConstraints left on, so they would still work just as they would in a springs-and-struts window.
It's possible that, under certain circumstances, Cocoa might add constraints. Certain Cocoa views, such as NSStackView, return YES from +requiresConstraintBasedLayout.
Are you using some of the most recent features in your window, such as title bar accessories?

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
}

Mac OS X Autolayout - expand to fill superview

I have a nib. It has an NSScrollView with an NSTableView inside it. I would like this tableview to automatically expand to fill its entire superview.
I'm trying to use autolayout but I have no idea how to add the constraints. Since the nib has no view objects other than the scroll view - I don't get how you reference the superview.
None of the auto layout buttons at the bottom of the nib editor give me any options, everything is grayed out.
I'm using Interface Builder, Xcode 5, OS X 10.9.2.
Not iOS!
You can't. But you probably have the code where this view controller is instantiated. There you need to add constraints manually.
To get started have a look at the WWDC sessions, in particular
Auto Layout by Example (https://developer.apple.com/videos/wwdc/2012/)
Best Practices for Mastering Auto Layout (https://developer.apple.com/videos/wwdc/2012/)
The learning curve is a bit steep but it'll be worth your while.
Check out the Content Hugging setting for your view to make sure it's allowed to expand.
Also check the warnings in the Interface Builder editor and let it add missing constraints if you're not sure what to do.
And always pay attention to the Autolayout warnings in the Xcode console!
This blog post has some nice hints as well:
http://oleb.net/blog/2013/03/things-you-need-to-know-about-cocoa-autolayout/
Some more specific tips:
Autolayout is enabled for your .nib/.xib, right? Just making sure.
The super view is referenced kind of implicitly when you set the constraints for your view, you don't set constraints for the top-level view in the .xib (if I recall correctly, no Xcode at hand right now).

Cocoa OSX custom widgets/controls

I want to know how can I create custom widgets/controls in Cocoa.
Not a full tutorial, but some guidance on what to start looking into. I'm confused by custom views, Core Animation, etc. I feel lost.
I see a lot of cool looking controls, like in Reeder App, or Sparrow etc. For example:
The left side is a collapsable menu that includes animations etc. How can I achieve something similar? I thought of using a WebView + HTML + JavaScript, but that doesn't seem like a very optimized solution.
Controls are views, so if custom views confuse you, you'll need to get that figured out before moving on to custom controls. Although you should really read the entire View Programming Guide, the section called Creating a Custom View will get you started on creating your own views. Try creating a simple view that draws a circle, for example, or the time.
When you've got views figured out, move on to custom controls. Most controls work about the same way. The user touches them, and the control responds by: a) tracking the user's input, b) changing its value, c) sending its action message to its target, and d) giving the user some feedback by redrawing itself. To get started, first make sure that you know how to use controls. Reading Control and Cell Programming Topics should help, and the section titled Subclassing NSControl covers (obviously) creating your own subclasses.
The example you provided is pretty clearly Apple's Mail.app. The view on the left side of the window might be an instance of NSOutlineView, or it might be a custom class. Either way, NSOutlineView would be a good starting point if you want to duplicate that functionality. NSOutlineView is a subclass of NSTableView, which in turn is a subclass of NSControl, which in turn is a subclass of NSView. Read Outline View Programming Topics for help getting started -- tables and outlines are extremely useful, but also more complicated to use than basic controls like buttons and text fields.
I know it's only a part of the UI, but I've recently coded something similar to the sidebar. If you look though the source-code it may give you some help on learning how to use custom controls and cells.
You can check it out on Github:
https://github.com/iluuu1994/ITSidebar

Resources