NSSplitViewController based application almost never launches with the correct size - macos

I have this app that uses a NSSplitViewController as the root and has a NSTabViewController connected as its detailViewController.
This app is set to launch at 1024x768. The left pane should launch at 320x768 and the right pane (where the tabViewController is), should launch at 704x768.
From 10 times I run this app, 9 times it will launch with the incorrect size (about 500x500). Other strange thing is that this app should not be scalable, but if you hover the mouse near the window border you see cursor indication to scale.
I want this to launch at the correct size and have no scalable option.
Both of these settings are on interface builder but are being ignored.
You can download a sample project that demonstrates the problem, here. Stop and run the project several times to see the problem.
How do I solve this?

I couldn't say for sure what's causing the problem, but one way you may be able to solve it is to add some constraints. Interface Builder doesn't allow you to constrain the default NSView instances that it inserts into the left and right panels of the split view, so you'll need to add your own. The screen-shot below
is taken from your demo, but after I've done the following:
Added a subview to the left split (My Content View), and pinned it's edges to the edges of its superview (the view Xcode automatically adds to the splitview)
Added an explicit width constraint of 320 pixels to My Content View
When I load the app both splits are visible, the divider doesn't budge, and the window can't be resized.
Update - a better solution
Although constraints are one way to solve this problem, I think the root of the problem lies in a bit of unexpected behaviour in Interface Builder. When you drag an NSSplitViewController object onto the canvas, and make it the target of the window controller's content window relationship, the split-view controller's view outlet is not actually set. One consequence of this appears to be that, when you load the app, the divider will appear to be right over to one side. To resolve this, set the aforementioned view outlet to point at the split view:
I've created a demo project with a setup similar to that in the questioner's demo app.

For reference, the same problem occurs if the window content segue points to an NSTabViewController scene. New windows open with a size of 500x500.
I solved it by placing a plain view controller with a container view between my window and my main tab view controller. The window will then use the size of the container view as initial size.
Here is what I did in detail:
Added a new view controller scene to the storyboard
Made that view the size I want my window to use initially
Added a container view to the new view controller scene & added 4 constraints to have the container cover the view completely
Connected the window's content segue to the new view controller
Finally connect the container view to my actual tab view controller scene
Before:
[Window Controller Scene] → [Tab View Controller]
After:
[Window Controller Scene] → [View Controller Scene] → [Tab View Controller]
(with Container View)

Related

OS X App - Cannot Resize App Window

I have an OS X app originally built using Xcode 4, now using Xcode 7. When "springs-and-struts" was superseded by constraints, I reworked the UI to use constraints. Simple enough, and seemed to work well.
Fast forward two years after first release, and for the second release I needed to add controls and increase the height of the main app view. Unfortunately, my test team is using smaller screens and cannot see the whole view. They need to resize vertically.
Problem - even though the resize controls box is checked, the window cannot be resized. The controls do not show at run time. I tried
Setting lower minimum window content size height, but that did not change anything.
Changing content compression resistance did not change anything.
I am thinking this issue has something to do with constraints.... Any ideas on how to get resize to work?
Edit: After playing with a new test app some, I am more certain the problem is due to constraints. I have a control where I have constrained leading and trailing space to superview and width - there went horizontal resize.
I really need to have a view where the user can resize the window, but scroll the content. However, in this case, the content is other controls. I think on iOS, I would use a UIScrollView. On OS X, I have tried a scroll view control and have tried embedding in a scroll view, and neither have the desired effect.
I had the same issue and solved it by adding a view to be used as a "container" in the view controller.
Pin the top left corner of the "container view" to the view controller (leading space 0 and top space 0). Add equal width and height constraints on the "container view" to the view controller. Then move all your objects into the "container view" and add your object constraints on the "container view" not the view controller.
In my case, it happened in this way (Xcode 13.1).
I mistakenly added a view from IB outside of the window view hierarchy. The new view was added as a separated object (a top node in the interface builder file). I added the new view into the window by drag-n-drop.
I found the new view had different behaviours, for example, I couldn't set the top space constraint. With this view in the view hierarchy, I couldn't change the window size (content view size) at all.
I removed the view and added another in the view hierarchy, it worked as normal.
I think IB initialises the view differently if it is a separated object (top node of the interface builder file).

How is a subview of an NSView dynamically positioned?

I am attempting to change the coordinates of an NSButton that is contained within a parent NSView and something is clearly not working, because the button position does not change. Both elements are defined in a nib file and the parent view has animation applied to it using CoreAnimation.
I have tried the following.
button.frame.origin.x = 500
and...
var frame:CGRect = button.frame
frame.origin.x = 500
button.frame = frame
Even with the animations disabled, I can not seem to dynamically position the subview. Is there some feature that prevents children views from being positioned programmatically?
Please note that I am using Swift with XCode 6.3.1.
I'm guessing you're using AutoLayout constraints, given you're using the latest tools.
If so, setting a subview's frame directly won't work the way you're expecting (if it does anything at all, it'll cause strange drawing glitches / flashing when mixed with animation). You have to create outlets for your layout constraints and modify them.
If you're not using AutoLayout, I suggest having a look at your button outlet to make sure it's actually connected (ie, you're not talking to nil). Even if the outlet is connected, make sure it's not nil at runtime - you may be trying to talk to the button before the nib is loaded and the outlet / action connections are restored.

Rotating iPhone 6 Plus results in gray detail view controller?

I am experiencing an odd issue with split view controllers, and I've been able to recreate the issue very easily in a brand new app. If you rotate the iPhone 6 Plus to landscape the master and detail view controller become visible on screen, but if you then rotate back to portrait and switch tabs then rotate to landscape, the detail view controller will be completely gray as if nothing is there. This is logged to console: <Error>: CGImageCreate: invalid image size: 0x0 I would like to know if you know the cause or what can be done to fix this bug.
The setup is a UITabBarController with three tabs, each tab is the default UISplitViewController dragged out via Interface Builder. Subclass UISplitViewController and change the 3 split views to that class. Set the split view controller's delegate to self in viewDidLoad. Then return YES from splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:. This sets it up so that the master remains visible after rotating back to portrait instead of the default details view controller.
To reproduce the bug: Launch app in portrait on iPhone 6 Plus, rotate to landscape, rotate back to portrait, tap the second or third tab, rotate to landscape
Bug: The detail view controller is completely gray
Expected: The detail view controller should appear like it normally does if you launch the app, tap a different tab, then rotate to landscape
Additional info: Once this occurs, if you rotate back to portrait and then go back to a different tab it will also show a gray detail controller upon rotation to landscape. The app must be force quit and relaunched to see those split views in landscape.
Now I noticed if you open the app and the first tab is displayed, if you rotate the iPhone 6 Plus to landscape, it actually initializes every single master and detail view controller in the tab bar controller (5 additional view controllers) - it stops at breakpoints set in viewDidLoad if you subclass those controllers. I expected it to only initialize the detail view controller for the split view controller that's displayed on screen. I'm wondering if this is expected behavior? I believe it could be linked to this bug.
My question is, is this gray screen a bug in iOS, or is there a problem with this setup, or is there something that can be done to prevent this from occurring?
How it should appear:
How it appears when following the above steps:
I encountered some thing strange myself, while working on my Multiple Detail Views sample. I was actually getting 2 Table Views displayed instead of 1 Table View and 1 Detail view in 6+ simulator. I could fix it by returning my detail view controller from separateSecondaryViewControllerFromPrimaryViewController method of SplitViewController delegate.
My code is like this:
func splitViewController(splitViewController: UISplitViewController, separateSecondaryViewControllerFromPrimaryViewController primaryViewController: UIViewController!) -> UIViewController? {
if let primaryAsNavController = primaryViewController as? UINavigationController {
if let topAsTableViewController = primaryAsNavController.topViewController as? TableViewController2 {
//Return Navigation controller containing DetailView1 to be used as secondary view for Split View
return (UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("detail1Nav") as UIViewController)
}
}
return nil
}
For me it was happening only when TableView2 was TopViewController, so I am checking for it and if true I am initialising my intended DetailViewController and returning it.
Hope this approach works for you as well.

Xcode - Nested storyboard views - hide/show?

I have a reasonably complicated UIView which contains several nested views which are displayed according to a variety of responses - all are laid out in a storyboard.
Is there a way to hide a view in the foreground to work on a view in the background? As its really fiddly selecting particular elements to arrange / style!?
I've been trying to figure out a nice way to do this, some function like hiding the view and its subviews from the storyboard (not from the actual application), but couldn't find anything.
This is not the nicest of ways but it is how I do it at the moment...
What I do is select the views I want to "hide" from the document outline and add a constant (screen width/height) value to its x/y origin value to push them out of the screen. I also change the document label for those views (Identity Inspector > Document > Label) to something like "Hidden" so I can later search for the "hidden" views from the document outline and put them back where they belong.
I have 2 work arounds.
A) Change the View Controllers size to freeform. Set its size to be really large so I can space out the views.
B) Use the sort order of the views Document Outline (lowest is front most) and add an image view (same as the view background) under the first view to block the others. Then delete it after finishing my edits.
or xcode developers could just simple add a design-visible checkbox for views and controls.... but ill take my rants somewhere else.
In xcode 7 you can do it from the storybord
for more details
https://stackoverflow.com/a/25213491/4879683
Maybe this could help you :
Open your storyboard in the Finder and edit it with a simple text editor (not xcode).
You will see it's just a xml file.Look for the view you want to hide, and add hidden="YES" in the parameters list.
That's what I do on my own project.
In Xcode when you select your storyboard, you have a panel that displays all your view controllers and their hierarchy. If you change the order of the elements you change the background/foreground order.
You can add extra views to the scene dock.
These views get initiated along with the view controller, but are not added to the view controller's view hierarchy. You can reference them using IBOutlets.
e.g. I have a full screen loading view that I added to the scene dock instead of covering up the view controller in the storyboard. I can add the loading view to the view controller's view hierarchy in code:
#IBOutlet weak var loadingView: UIView!
...
loadingView.frame = view.bounds
loadingView.autoresizingMask = UIViewAutoresizing.FlexibleWidth.union(UIViewAutoresizing.FlexibleHeight)
view.addSubview(loadingView)
Reference: https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/Chapters/AddViewsToDock.html

Resizing view controller acording to tabbar controller

In my application, I have developed one wizard in which I am providing a way for the user to setup his details one-by-one. After finishing all steps, the user will be redirected to the screen where TabBar will come into the picture.
The problem here is that the user can access the same view controllers with the wizard (without TabBar controllers) and normal flow (which is with tabbar controller). In the wizard, I am using a view controller of size 320x480 and the same in normal flow. But whenever I load any view controller using TabBar the 44 pixel view from bottom side gets hidden behind TabBar.
I know the I can manually set the view size, by detecting whether TabBar is present or not, but here in this case number of view controllers are more and its already designed of size 320x480.
I had tried with all methods given in Apple's documentation but none of it seems to work for me.
Following are the methods I have tried, along with some xib settings.
[self setWantsFullScreenLayout:YES];
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self.view setAutoresizesSubviews:YES];
[self.navigationController.view setNeedsLayout];
Is there any way to set the height of a view controller according to whether that TabBar is present or not?
In my case, (Status bar/Nav bar/Hidden tabbar) this worked
//Add this in your ViewController
self.edgesForExtendedLayout = UIRectEdgeBottom;
self.extendedLayoutIncludesOpaqueBars = YES;
However Barry's answer is better if you use Storyboard but I could not for this VC.
Tried for iOs7 and iOs 8.
If you're using storyboards, select each tab VC and clear the box for View Controller > Extend Edges > Under Bottom Bars.
I wasn't able to find a good answer for this, so I ended up adding a BOOL, hasTabBar. I set it true or false based on where I create my view, and use that to calculate my layouts.
frame.size.height -= (hasTabBar*50); // works nicely.

Resources