The following steps for UIScrollView+autolayout has been working for me, but not in the iOS8/Xcode 6 preview: (using storyboard, size class enabled):
add a scrollview to the root view.
pin zero spaces to all edges of super view.
add a UIView (contentView) to the above scrollview.
pin zero spaces to all edges of the scrollview
add some widgets to contentView and change the height of the contentView to 2000.
=> this contentView scrolls in iOS 7, but I cannot get the same steps working in iOS 8 preview.
Even it seems working in iOS 7, it is possible that I may not doing the right way? Any suggestions?
I'm surprised not to have seen more comment about this. Scroll view internal autolayout is largely broken in iOS 8 (as seeded up to the time of this writing).
EDIT This was fixed in seed 5, so this note should be ignored!
The rule is supposed to be (see https://developer.apple.com/library/prerelease/ios/technotes/tn2154/_index.html) that if the content of a scroll view (its subview or subviews) is pinned to all four bounds of the scroll view, it sets the content size.
In iOS 8, however, this fails - but in an odd way. It fails only if the constraints determining the height and width of the subviews are all absolute as opposed to intrinsic.
Thus, for example, consider the code at the bottom of that tech note, where a scroll view and a really big image view are created in code (here it is; I have corrected a small typo where an at-sign was dropped):
- (void)viewDidLoad {
UIScrollView *scrollView;
UIImageView *imageView;
NSDictionary *viewsDictionary;
// Create the scroll view and the image view.
scrollView = [[UIScrollView alloc] init];
imageView = [[UIImageView alloc] init];
// Add an image to the image view.
[imageView setImage:[UIImage imageNamed:#"MyReallyBigImage"]];
// Add the scroll view to our view.
[self.view addSubview:scrollView];
// Add the image view to the scroll view.
[scrollView addSubview:imageView];
// Set the translatesAutoresizingMaskIntoConstraints to NO so that the views
// autoresizing mask is not translated into auto layout constraints.
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
// Set the constraints for the scroll view and the image view.
viewsDictionary = NSDictionaryOfVariableBindings(scrollView, imageView);
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[scrollView]|"
options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[scrollView]|"
options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[imageView]|"
options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[imageView]|"
options:0 metrics: 0 views:viewsDictionary]];
}
That code works (assuming you have a really big image), because the image view is sized by intrinsic constraints. But now change the last two lines, like this:
[scrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[imageView(1000)]|"
options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[imageView(1000)]|"
options:0 metrics: 0 views:viewsDictionary]];
Now what you have is a scroll view that is scrollable on iOS 7 but is NOT scrollable on iOS 8. Further investigation shows that this is because the content size remains at (0,0); it does not respect the absolute width and height constraints of the content view.
Use following step for UIScrollView + AutoLayout
Add scroll view to the root view
Add contain view to above scroll view
Add Following constraint for scroll view
Trailing space to super view = 0
Leading Space to super view =0
Top space to super view = 0
Bottom Space to super view = 0
Add Following Constraint for contain view of scroll view
(in this case scroll view is super view)
Trailing space to super view = 0
Leading Space to super view =0
Top space to super view = 0
Bottom Space to super view = 0
Height of contain view (if you are using vertical scrolling) otherwise width of contain view (if you are using Horizontal scrolling).
Horizontal canter alignment (if you are using vertical scrolling) otherwise vertical canter alignment (if you are using Horizontal scrolling).
Related
I have the following code:
[[ticketsListScrollView documentView] setFrame: NSMakeRect(0, 0, [ticketsListScrollView frame].size.width, 53 * [tickets count])];
[[ticketsListScrollView documentView] setFlipped:YES];
for(int i = 0; i < [tickets count]; i++) {
TicketsListViewController *viewController = [[TicketsListViewController alloc] initWithNibName:#"TicketsListViewController" bundle:nil];
viewController.dateLabelText = tickets[i][#"date"];
viewController.timeLabelText = tickets[i][#"time"];
viewController.subjectLabelText = tickets[i][#"title"];
NSRect frame = [[viewController view] frame];
frame.origin.y = frame.size.height * i;
[viewController view].frame = frame;
[[ticketsListScrollView documentView] addSubview:[viewController view]];
}
if the list is large enough (many views), the NSScrollView starts at top-left, which is great. For less views (the views do not take the whole documentView, then NSScrollView starts at the middle.
Any idea why?
Thank you!
Views are not flipped by default, which means your document view is being pinned to the lower-left corner (the default, non-flipped view origin) of the scroll view. What you're seeing is a view not tall enough to push the "top" subview to the top of the scroll view. I see you tried flipping this view, so you already know about this, but you're not doing it correctly.
I'm not sure why you're not getting an error or a warning when calling -setFlipped: since the isFlipped property is read-only. In your document view (the view that's scrolled, and in which you're placing all those subviews), you can override it:
- (BOOL)isFlipped {
return YES;
}
Of course you'll have to put this in a custom NSView subclass and set that as your scroll view's document view's class in IB if you're not creating it at runtime. You'll also need to adjust the frames you use for layout, since you're currently expressing them in the coordinate system of the scroll view's frame. You should be expressing them in your container/layout view's bounds coordinates, which will also be flipped, and so, likely different from your scroll view's coordinates. You'll also need to implement -intrinsicContentSize (and call -invalidateIntrinsicContentSize when adding/removing subviews) so auto-layout can size the container appropriately.
I want the screen only shows one tableveiwCell with headerView.I added a UIView as header view on the top of UITableView with "Size classes" in storyboard(Just drag a UIView on the top of the UITableView), it can compatible with all devices screen size in this way.
So change header view's height by constraints is what i want to. but when i try to do that, i cant set constraints for the headerView(Xocde doesn't enable me select any constrains in storyboard, Image 2 below). As below.
Any ideas, thanks!
The project code is here: https://github.com/williamhqs/AutoLayoutTableViewHeaderView
EIDT:
Seems it still can't be set in storyboard.
Then i will have to change table header view's frame by code then update constraints.
UIView *v = self.tableView.tableHeaderView;
CGRect fr = v.frame;
fr.size.height = [UIScreen mainScreen].bounds.size.height -100;
v.frame = fr;
[self.tableView updateConstraintsIfNeeded];
Then i will have to change table header view's frame by code then update constraints.
UIView *v = self.tableView.tableHeaderView;
CGRect fr = v.frame;
fr.size.height = [UIScreen mainScreen].bounds.size.height -100;
v.frame = fr;
[self.tableView updateConstraintsIfNeeded];
i think that UIView doesn't have any superview So that it is not showing the constraints for view.
I have a superview to wich I add two subviews (subview1 and subview2).
I want subview1 to have same width as superview and stretch with superview and also have a height of 30px and align to the top of superview. This I can get working with the following code:
NSDictionary *metrics = #{#"height":[NSNumber numberWithFloat:30.0f]};
NSDictionary *views = NSDictionaryOfVariableBindings(subview1);
NSArray *tabContainerConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[subview1(==height)]" options:NSLayoutFormatAlignAllTop metrics:metrics views:views];
[superview addConstraints:tabContainerConstraints];
metrics = nil;
tabContainerConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|[subview1]|" options:(NSLayoutFormatAlignAllLeading | NSLayoutFormatAlignAllTrailing) metrics:metrics views:views];
[superview addConstraints:tabContainerConstraints];
Then I want subview2 to also have same width as superview and stretch with superview and also I want subview2 to align its top to subview1's bottom and then I want subview2 to fill all of the remaining height of superview (align bottom to bottom) and stretch in height with superview. I try to do this with this code:
NSDictionary *metrics = nil;
NSDictionary *views = NSDictionaryOfVariableBindings(subview2);
NSArray *tabContainerConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|[subview2]|" options:(NSLayoutFormatAlignAllLeading | NSLayoutFormatAlignAllTrailing) metrics:metrics views:views];
[superview addConstraints:tabContainerConstraints];
views = NSDictionaryOfVariableBindings(subview1, subview2);
tabContainerConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[subview1][subview2]-0-|" options:0 metrics:metrics views:views];
[superview addConstraints:tabContainerConstraints];
But strange things happens... subview is always aligned to with its top to subview1's bottom and also has the full width of superview so this is good. But height is strange... When first drawn/displayed subview2 has a very limited height, somewhere between 20-30 pixel it seems, but I can force a redraw by switching to another tab/view and back and then it is drawn in full/correct height. My subview2 is a NSTextView, and when I type in text strange things happen, my subview2 suddenly does not take up all height and is no longer aligned with the bottom of superview.
I hope my explanation is ok, if not please ask any question. Any ideas on how to fix this? I thought the |-0-[view]-0-| would do the trick?
Thank you
Søren
It's not clear from your question whether you're running all of that code or the first snippet is for one view only and the second snippet is for two views.
You should be combining the two VFL strings into one, and you don't need some of the layout options that you are supplying. If you only have one view in the VFL string, the layout options are meaningless.
I'd suggest the following code for creating your constraints:
NSDictionary *metrics = #{#"height":[NSNumber numberWithFloat:30.0f]};
NSDictionary *views = NSDictionaryOfVariableBindings(subview1,subview2);
// Horizontal layout for subview 1
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[subview1]|" options:0 metrics:metrics views:views]];
// Vertical layout - the options here mean that subview2 will be the same width as subview1
[superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[subview1(==height)][subview2]|" options:NSLayoutFormatAlignAllLeft | NSLayoutFormatAlignAllRight metrics:metrics views:views]];
You may be having some issues with the intrinsic size of the text view, if it behaves differently when it has content inside it or not. Or, the superview itself may be changing its size - what are the constraints on the superview? You will need to set some borders / background colours or use an introspection tool to determine which frames are going funny.
I'm trying to create a NSTableView inside a NSScrollView (the standard configuration, that is) in code, using auto layout. I can't figure out how to make this work.
Here's my loadView:
- (void)loadView
{
NSView *view = [[NSView alloc] init];
NSScrollView *tableScroll = [[NSScrollView alloc] init];
NSTableView *fileTable = [[NSTableView alloc] init];
[tableScroll setDocumentView:fileTable];
[tableScroll setHasVerticalScroller:YES];
[tableScroll setHasHorizontalScroller:NO];
fileTable.delegate = self;
fileTable.dataSource = self;
[fileTable setHeaderView:nil];
[fileTable setAllowsColumnReordering:NO];
NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:#"column1"];
[fileTable addTableColumn:column];
[tableScroll setTranslatesAutoresizingMaskIntoConstraints:NO];
[fileTable setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:tableScroll];
NSDictionary *topViews = NSDictionaryOfVariableBindings(tableScroll);
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[tableScroll]|" options:0 metrics:nil views:topViews]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[tableScroll]|" options:0 metrics:nil views:topViews]];
self.fileTable = fileTable;
self.view = view;
}
What happens is that my table view's frame will be always equal to the bounds of the NSClipView. The view is inside a window and gets resized with it, and when I do that it'll resize the scrollview, the clip view and the table, but I can never scroll anywhere.
Looking at constraints I get, the NSScrollView gets constraints that set the clip view to fill it, the clip view has no constraints at all and the table view has a bunch of constraints related to the NSTableRowViews inside it.
If I add a constraint like |[fileTable(>=500)] to the clip view I'll get 500 pixels of NSTableView, but obviously I don't want to do that.
Even though this was answered by the poster in the comments above, I thought I’d put the answer here (having run into the same issue). If you are adopting auto layout, you would typically uncheck “Translates Mask Into Constraints” in the xib. However, for classes like NSScrollView and NSTableView, you should generally let them manage their own internal views by setting their translatesAutoresizingMaskIntoConstraints property to YES. It is still ok to set constraints that are external to these views, i.e. to resize in relation to their superview.
If you set translatesAutoresizingMaskIntoConstraints to NO, then you will need to supply constraints for all of the internal views, which unless you specifically need custom behavior (almost never), you will not want to do. This was the specific problem above.
An obvious side effect of not setting this correctly is that a table (for example) will not properly scroll beyond what is visible in the view.
I need to make a simple scroll view in xcode with width of 280 and height of 80 and with images inside thats scrolls horizontally. i want to make this programmatically.
I assume you mean the UIScrollview, which has a guide written by apple found here:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIScrollView_Class/Reference/UIScrollView.html
A guide that I personally used was this one:
http://idevzilla.com/2010/09/16/uiscrollview-a-really-simple-tutorial/
I'll take you through the quick basics of adding the scrollview to your view and adding images to it.
I'm guessing you're new to Objective C, so I'll give you a quick guide. Firstly, you'll want to make a UIScrollView object. This is done by declaring the following:
UIScrollView *aScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake (0,0,320,250)];
You'll notice I set the frame. The first two numbers of CGRectMake give you the x and y origin of the point while the last two numbers are for how wide and tall you want your object to be.
Afterwards, you'll want to add images to it. You'll need a UIImageview.
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 250)];
Note that I positioned the image at 0,0, giving it a height of a 250 and a width of 320. This ensures that it fills entire scrollview's initial view.
imageView.image = [UIImage imageNamed:#"foo.png"];
You'll attach an image to the imageView. But wait, there's more. So far you've created these objects but have not yet associated them with the view. So if we are in a ViewController class (you'll have to look up what that is), the ViewController contains a view. We can attach our objects to the view.
[aScrollView addSubview:imageView]; // Adds the image to the scrollview
[self.view addSubview:aScrollView]; // Adds the scrollview to the view.
If you want to add more images, you have to add them at different x origins. So our first added image was at 0,0. Our next added image should be at 320,0 (because the first image took up 320 pixels width).
UIImageView *secondImageView = [[UIImageView alloc] initWithFrame:CGRectMake(320, 0, 320, 250)];
secondImageView.image = [UIImage imageNamed:#"bar.png"];
[aScrollView addSubview:secondImageView];
There are a number of options for scrollview that you will want to explore. The ones I found useful were:
aScrollView.delegate = self; // For gesture callbacks
self.pagingEnabled = TRUE; // For one-at-a-time flick scrolling
self.showsHorizontalScrollIndicator = NO; // Cleaner look for some apps.
self.alwaysBounceHorizontal = TRUE; // Look it up.