Resizing a custom view with the window - objective-c

So I've seen this question asked a couple times, but haven't come across an answer that solves my problem. Right now I basically have MainMenu.xib with a label centered on the top, a large custom view and a button centered on the bottom that switches subviews of the custom view (see picture below). I've set the window and the custom view to autoresize subviews in the interface builder and all the buttons, labels, etc. have constraints relating them to the sides of the view, but when I resize the window, the contents of the subview do not resize. I think the custom view is resizing with the window because when I switch subviews with the button, the subview that loads is resized with the window, they just aren't resizing with the window in real time.
MainMenu.xib:
Normal subview:
After window is expanded:
Subview switched and then switched back:
CONSTRAINTS:
MainMenu.xib:
BlockViewController.xib:

Figured it out! Really simple, just one line of code in the awakeFromNib method of each ViewController subclass:
[self.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];

From the pictures and the description in the comments it seems that the problem is that the custom view does not get any layout constraints to determine how to resize the subview.
The following piece of code (typed directly in the browser, so beware) should provide the necessary constraints to "glue" the borders of the subview to the custom view.
[self.customView addSubview: self.blockSubView.view];
self.blockSubView.view.frame = self.customView.bounds;
NSView *blockSubView = self.blockSubView.view;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(blockSubView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|[blockSubView]|"
options:0
metrics:nil
views:viewsDictionary];
constraints = [constraints arrayByAddingObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat:#"V|[blockSubView]|"
options:0
metrics:nil
views:viewsDictionary]];
[self.customView addConstraints: constraints];
There is more about this in the docs. Notice the tricks for debugging - very useful to the point of being indispensable.

Related

NSLayoutConstraint Prevents NSWindow Resizing

I am adding some simple constraints to an NSView that I have in my main NSWindow but it is causing problems.
Normally I can resize my application window (just like any Safari/Finder window etc.). I add a simple NSView to my window like so:
[self.blackDimOverlay setFrame:NSMakeRect(0, 0, self.window.frame.size.width, self.window.frame.size.height)];
[self.blackDimOverlay setAlphaValue:0.5f];
[self.window.contentView addSubview:self.blackDimOverlay];
This works as expected. I then add two constraints to this view so that it stays the full size of the window when resized.
NSDictionary *viewsDict = NSDictionaryOfVariableBindings(_blackDimOverlay);
NSArray *constraintHorizontalOverlay = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[_blackDimOverlay]-0-|" options:0 metrics:nil views:viewsDict];
NSArray *constraintVerticalOverlay = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[_blackDimOverlay]-0-|" options:0 metrics:nil views:viewsDict];
[self.window.contentView addConstraints:constraintHorizontalOverlay];
[self.window.contentView addConstraints:constraintVerticalOverlay];
However, after adding these two constraints, they lock my window up, so I can't resize it anymore. Everything else works as normal, but the constraints block any window resizing.
How can I keep my subview the full-size of my window while being able to resize my window?
Thanks.
There are additional constraints on _blackDimOverlay which dictate its size and are of higher priority than NSLayoutPriorityDragThatCanResizeWindow. You can investigate using:
NSLog(#"%#", [self.window.contentView constraintsAffectingLayoutForOrientation:NSLayoutConstraintOrientationHorizontal]);
NSLog(#"%#", [self.window.contentView constraintsAffectingLayoutForOrientation:NSLayoutConstraintOrientationVertical]);
Most likely, you forgot to do [_blackDimOverlay setTranslatesAutoresizingMaskIntoConstraints:NO].
Alternatively, the view class of which _blackDimOverlay is an instance defines an intrinsic size and its hugging and compression-resistance priorities are higher than NSLayoutPriorityDragThatCanResizeWindow. You would need to reduce those priorities using -setContentHuggingPriority:forOrientation: and -setContentCompressionResistancePriority:forOrientation:.

Shifting view after displaying modal - possibly AutoLayout related

I present a simple view with a couple of labels and a button, all inside a UIScrollView and laid out using auto layout.
The button presents another view, which includes a navigation item for dismissal.
After dismissal, though, the content of the original UIScrollView is offset. Strangely, the amount by which it is offset seems related to the scroll position at the time of presentation.
The demo project here is a small example of this issue. Run it in the iPhone simulator and scroll to the bottom to use the 'modal' button. After dismissing the modal attempt to scroll back to the top - the issue should be clear.
Or refer to the scroll bar in the images below to see the problem.
BEFORE PRESENTATION
AFTER PRESENTATION
I'm not an expert in AutoLayout, but I fixed it by adding the label & button constraints to self.view instead of self.scrollView.
For example:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[l1]"
options:0
metrics:nil
views:#{#"l1":self.l1}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[l1]"
options:0
metrics:nil
views:#{#"l1":self.l1}]];
Why this fixes it... have no idea :D
I've had this same problem, and after much investigation it appears to be a bug in UIKit relating to scrollviews and AutoLayout. Here's the 'fix'...
In viewDidDisappear:, save the current scrollview contentOffset to a property, and reset it to zero:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.previousContentOffset = self.scrollView.contentOffset;
self.scrollView.contentOffset = CGPointZero;
}
Then, in viewWillAppear:, reset the content offset back to what it was previously. I had to dispatch this onto the main queue to get it to work correctly:
- (void)viewWillAppear:(BOOL)animated
{
if (!CGPointEqualToPoint(self.previousContentOffset, CGPointZero))
{
dispatch_async(dispatch_get_main_queue(), ^{
self.scrollView.contentOffset = self.previousContentOffset;
});
}
}

UITableViewCells scroll horizontally using auto layout when changing to portrait mode

I am creating a screen that is primarily a UITableView with cells that have differing controls and layouts. I had the tableview working great until I switched to portrait mode where it allowed the tableview cells to scroll horizontally. The cells should never scroll horizontally; only vertically. When I navigate to the screen and I'm already in portrait mode it works fine, even going to landscape and back to portrait.
Some things to note are that I'm using and iPad, storyboards, auto layout, and the UITableViewCell is custom and does not have a nib (though it does the same thing when I tried using a nib). All controls and layout constraints are added programmatically. I am using a custom view controller with a view inside of it. Inside of the view I have the tableview and a toolbar. If I remove the view so that the tableview is directly under the custom view controller it works fine, but then I can't have the toolbar.
Here is the test code I am using to troubleshoot the problem:
// Remove all existing subviews
for(UIView *subView in self.contentView.subviews)
{
[subView removeFromSuperview];
}
UILabel *testLabel = [[UILabel alloc] init];
testLabel.text = #"Test";
[testLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
self.contentView addSubview:testLabel];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(testLabel);
NSString *format = [NSString stringWithFormat:#"V:|-[testLabel]-|"];
NSArray *constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:format options:nil metrics:nil views:viewsDictionary];
//[self.contentView addConstraints:constraintsArray];
The scrolling works as intended until I uncomment the line to add the constraints. This code is in a method of my custom cell and gets called every time a cell is dequeued. I've tried adding the contraints individually, without using the visual format, with no luck. I have also done some research and found the code that removes the contraints from the cell and adds them to the contentView, but that hasn't helped either.
What do I need to do to keep the cells from scrolling horizontally when changing from landscape to portrait mode?
MORE INFO:
After some more debugging I found that the table view's content size is not shrinking horizontally. The width is staying at 1024.

UIScrollView not scrolling when adding item in IB and code

When I create a Scrollview on my scene, and then add a button to scene in IB. Then I go into the code, set the content size, enable user interaction and add another button. When I run the program in the simulator the Scrollview does not work, if I remove the button that is in IB on the scene it works just fine. Is it not possible to add items to the scrollview both in IB and programmatically?
EDIT: I thought it may be something in the app I already had. So I decided that I would create anew project and all it has in it is code, and the scene picture below. It is indeed added below the ScrollView.
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setTitle:#"My Button" forState:UIControlStateNormal];
myButton.frame = CGRectMake(100, 100, 150, 50);
[scrollView addSubview:myButton];
scrollView.userInteractionEnabled = YES;
[scrollView setContentSize:CGSizeMake(320, 1000)];
Here your scrollView is not scrolling due to the autoLayout, uncheck the auto Layout if you are not using.
I Just made a similar to your requirement. It is working fine, and after allowing autoLayout it just stopped scrolling.
The auto layout constraints fits to the visible part of the screen, if the objects in scrollView are more then screen size, it will scroll.
So my suggestion if you are not using autoLayout just uncheck it, and works fine.
Here is an helpful link: http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html#//apple_ref/doc/uid/TP40012166-CH1-SW19
Basically what this says is that, with auto-layout you don't and shouldn't have to use setContentSize:. Instead your inner view should have it's edges snapped to the edges of the scrollview. Let me demonstrate how I solved it.
scrollBackground : a view that contains every other view that needs to scroll in the scrollview.
[scrollview addSubview:scrollBackground];
[scrollview addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[scrollBackground(1000.0f)]|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(scrollBackground)]];
[scrollview addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[scrollBackground(==scrollview)]|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(scrollBackground)]];
The key here being those | at beginning and end of the VisualFormat String. These will tell the scrollview what is the contentSize. Of course if you have a contentSize smaller than the frame size of the scrollview it won't scroll in that direction. In my example this is true for width. It only scrolls up and down.
Make sure you set your content size in viewDidLoad or at some point after the view has already been loaded from the nib file.
As vishy Pointed out, your Button should be part of the ScrollView's Hierarchy, else you'll just be scrolling an empty view.
In the case you posted, the scrollview will not scroll because all of the content is visible. Try changing that last line to:
[scrollView setContentSize:CGSizeMake(50, 50)];
and it will start scrolling, because the content size is smaller than all of the content in the view.
check d2burke answer in this question
you should place your code in viewWillLayoutSubviews instead of viewDidAppear

How do I stop an NSView subview being resized with Auto Layout

I am having a problem with the new Auto Layout functionality.
I add an NSView called tableView as a subview of workingBox. workingBox also contains some other subviews that were added with Interface Builder
[self.window setFrame:frame display:YES animate:YES];
[workingBox addSubview:tableView];
My problem is that if any of the other subviews touch tableView then they will get resized in the animation. The subviews that are not near where tableView is being added remain the correct size. I have already tried setting NSViewNotSizable resizeMask on the subviews that are getting resized but it does not work. The only thing that works is moving the subview upwards (into the wrong position.
EDIT: I have added a constraint in code:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(divider);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[divider(==1)]"
options:0
metrics:nil
views:viewsDictionary];
[workingBox addConstraints:constraints];
This keeps the NSView at 1px height but if I lock it to the top it prevents the superview growing
I could not make this work by adding a constraint in code. However in Interface Builder I could see that there was a constraint for the NSView in question that was named 'Bottom Alignment'. I changed this to a priority of 1 in the inspector and it stopped the NSView getting stretched down when the window expanded. I think this auto added constraint that I mentioned in my comment above had been the problem all along.