Using Autolayout with NSScrollview - cocoa

My application contains a main container view in which three different views(subviewA, subviewB and subviewC) are swapped in and out. Both subviewA and subviewB are to resize with the window and I do this as follows
NSView *myCurrentView = [_myCurrentViewController view];
if ([[_myCurrentViewController title] isEqualToString:subviewA] ||
[[_myCurrentViewController title] isEqualToString:subViewB]) {
[_myScrollView setHidden:YES];
[myCurrentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_mainWindowView addSubview: [_myCurrentViewController view]];
[_mainWindowView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[myCurrentView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myCurrentView)]];
[_mainWindowView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[myCurrentView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myCurrentView)]];
}
This works fine, however for subviewC i would like to have the horizontal size of the subview, resize with the window, but use scrolling for the vertical. So, in IB I added a NSScrollView as a subview of my mainWindowView. I hide the scroll view if subViewA or subviewB are selected, but I don't know how to properly configure the constraints for subviewC. I figured I could add horizontal constraints simliar to subviewA or subviewB, since I woulk like the view to resize with the window, however I don't know what to add for constraints in the vertical to allow scrolling, so far I have this
[[_myCurrentViewController view] setTranslatesAutoresizingMaskIntoConstraints:YES];
[_myScrollView setHidden:NO];
[_myScrollView setDocumentView:[_myCurrentViewController view]];
[_myScrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[myCurrentView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myCurrentView)]];
Thanks

Related

uiscrollview autolayout refresh after move scroll

I have IBOutlet 2 property, scrollview and contentview, i create my view via xib, I Set all consraint inside contentview, now I need to put contentview inside scrollview, I try to do something like this:
[_scrollView addSubview:_scrollViewContent];
[_scrollView setContentSize:_scrollViewContent.frame.size];
_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
_scrollViewContent.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = NSDictionaryOfVariableBindings(_scrollView, _scrollViewContent);
NSArray *scrollViewLabelConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_scrollViewContent(_scrollView)]" options:0 metrics:nil views:views];
[_scrollView addConstraints:scrollViewLabelConstraints];
initially not work, contentview is a bit larger then scrollview, when I scroll horizontally I see the view in the correct way.
Where is the error?

UIScrollView Autolayout prevent from scrolling vertically

I'm wondering how to crop image inside UIscrollView with autolayout
I'm trying to make UIscrollView scroll only horizontally. if image is higher than view height it should be cropped. I've tried a lot properties but can't make all images inside uiscrollview same height as view to avoid scrolling vertically.
Do i miss something?
#import "WelcomeController.h"
#interface WelcomeController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (nonatomic,strong) NSArray *contentList;
#end
#implementation WelcomeController
#synthesize contentList =_contentList;
- (void)updateUI
{
UIScrollView* sv = self.scrollView;
id previousLab = nil;
for (UIView *lab in _contentList) {
lab.translatesAutoresizingMaskIntoConstraints = NO;
lab.backgroundColor = [UIColor blackColor];
lab.contentMode = UIViewContentModeScaleAspectFit;
[sv addSubview:lab];
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[lab]|"
options:0 metrics:nil
views:#{#"lab":lab}]];
if (!previousLab) { // first one, pin to top
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[lab]"
options:0 metrics:nil
views:#{#"lab":lab}]];
} else { // all others, pin to previous
[sv addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:[prev][lab]"
options:0 metrics:nil
views:#{#"lab":lab, #"prev":previousLab}]];
}
previousLab = lab;
}
// last one, pin to bottom and right, this dictates content size height
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[lab]|"
options:0 metrics:nil
views:#{#"lab":previousLab}]];
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[lab]|"
options:0 metrics:nil
views:#{#"lab":previousLab}]];
}
-(void)setContentList:(NSArray *)contentList
{
_contentList = contentList;
[self updateUI];
}
- (void)setupScrollView
{
UIScrollView* sv = [UIScrollView new];
sv.backgroundColor = [UIColor redColor];
sv.translatesAutoresizingMaskIntoConstraints = NO;
sv.pagingEnabled = YES;
sv.showsHorizontalScrollIndicator =NO;
sv.showsVerticalScrollIndicator = NO;
sv.bounces =NO;
[self.view addSubview:sv];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sv]|"
options:0 metrics:nil
views:#{#"sv":sv}]];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[sv]|"
options:0 metrics:nil
views:#{#"sv":sv}]];
self.scrollView = sv;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupScrollView];
//for testing
UIImageView *image1=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"welcome1.jpg"]];
UIImageView *image2=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"welcome2.jpg"]];
UIImageView *image3=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"welcome3.jpg"]];
self.contentList = [NSArray arrayWithObjects:image1,image2,image3,nil];
}
#end
Have you tried setting the height of the scroll view explicitly?
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_sv(123)]" options:0 metrics:nil views:views]];
You would need to replace the height above (123) with the height you need, obviously, as well as views.
Autolayout automatically manages the UIScrollView's contentSize (the scrollable area). So if you stick a subview in there with an intrinsic size that is larger than the height, it will increase the contentSize. I can think of two things:
Stick images in a plain UIView with the same height as the scrollview.
Subclass the UIImageView and override the intrinsicContentSize method to return a fixed height for all images. This seems like a poor solution, though.
I think you should be able to set the image view's frames to a fixed height (via constraints). Then add constraints to have the image views top and bottom fixed with x constant from the scrollview.
This will let the scrollview know its exact content size to use. As long as its frame, then, (determined by whatever constraints you give it in relation to its superview) is >= the image view's fixed heights, it won't scroll vertically.

UIScrollView + Autolayout issue

Considering the following structure:
| UIScrollView | UITextField | (UIScrollView on the left and a UITextField the right)
I am trying to add a UILabel into the UIScrollView and make the UIScrollView grow according to the UILabel size. The UITextFiled would then get smaller according to the grow of the UIScrollView.
- (IBAction)makeScrollViewGrow:(id)sender
self.myScrollView.translatesAutoresizingMaskIntoConstraints = NO;
self.myTextField.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *myLabel = [[UILabel alloc] init];
myLabel.text = #"THIS IS A LONG MESSAGE";
myLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.myScrollView addSubview:myLabel];
UITextField *theTextField = self.myTextField;
[self.myTextField removeConstraint:self.titleTextFieldWidthConstraint];
[self.myScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[myLabel]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(myLabel)]];
[self.myScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[myLabel]|" options:0 metrics:0 views:NSDictionaryOfVariableBindings(myLabel)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-20-[theSCrollView]-8-[theTextField]-20-|" options:0 metrics: 0 views:NSDictionaryOfVariableBindings(theSCrollView, theTextField)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[theSCrollView]|" options:0 metrics: 0 views:NSDictionaryOfVariableBindings(theSCrollView)]];
}
The issue here is that the UIScrollView is not being resized because the UITextField has a fixed width. If I remove programatically the width UITextField constraint the UIScrollView takes over the all width without considering the text Label (intrinsicContentSize).
Will I need to set the Scrollview contentsize manually or can I do this automatically using autolayout?
Thanks

Auto layout of custom programmatic UITableViewCell fails upon scrolling

I'm trying to use the new auto layout capability of iOS 6 on a custom UITableViewCell which has been implemented programmatically. I added the addConstraint calls, and it works properly at first-- until I scroll. When I come back to the cell after scrolling the layout is trashed. By trashed I mean the margins between fields are all wrong (too large, well beyond the size of the cell). I'm speculating this has something to do with the dequeueReusableCellWithIdentifier method leaving me with a "dirty" cell, the same way you find yourself needing to reinitialize fields within cells, but I can't seem to do anything to coax it to render properly again. I've tried calling [self.contentView updateConstraints] before returning the cell. I've tried destroying the constraints and recreating them. Not only does it not work, but if it's attempted in layoutSubviews it freezes in an endless loop of some kind. Any ideas?
Here's the code to establish the constraints. It's located in initWithStyle:reuseIdentifier:
[self.completedLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.nextSetHeaderLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.nextSetDetailLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.youWillLearnHeaderLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.youWillLearnDetailLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView removeConstraints:[self.contentView constraints]];
NSDictionary *views = NSDictionaryOfVariableBindings(_completedLabel, _nextSetHeaderLabel, _nextSetDetailLabel, _youWillLearnHeaderLabel, _youWillLearnDetailLabel);
[self.contentView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_completedLabel]-5-|"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_nextSetHeaderLabel]-5-|"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_nextSetDetailLabel]-5-|"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_youWillLearnHeaderLabel]-5-|"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[_youWillLearnDetailLabel]-4-|"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-5-[_completedLabel]-12-[_nextSetHeaderLabel]-0-[_nextSetDetailLabel]-12-[_youWillLearnHeaderLabel]-0-[_youWillLearnDetailLabel(>=20)]-1-|"
options:0
metrics:nil
views:views]];
I ran into this issue as well. If I wasn't dequeuing cells, everything seemed to work - scrolling, rotation etc. However, if I dequeued cells, then the layout started getting messed up. The only way I could get it to work was by overriding the cell's prepareForReuse method. In this method,
1. remove all the custom subviews
2. remove all constraints associated with those subviews from contentView
3. add subviews and constraints again
-(void) prepareForReuse
{
[self removeCustomSubviewsFromContentView];
[self.contentView removeConstraints:self.constraints] //self.constraits holds all the added constraints
[self setupSubviewsInContentView];
[self addConstraintsToContentView];
}
If there is a better way to do this, I would love to learn as well :) I believe the advantage of dequeing is that the tableView does not have to hold a large number of cells in memory - but, with this method, one has to go through the cost of essentially setting up the cell everytime you dequeue.
I had a similar problem, in case anyone is interested I've found a solution, see this question
What I've done:
- (void)awakeFromNib
{
[super awakeFromNib];
for (NSLayoutConstraint *cellConstraint in self.constraints)
{
[self removeConstraint:cellConstraint];
id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
NSLayoutConstraint* contentViewConstraint = [NSLayoutConstraint constraintWithItem:firstItem
attribute:cellConstraint.firstAttribute
relatedBy:cellConstraint.relation
toItem:seccondItem
attribute:cellConstraint.secondAttribute
multiplier:cellConstraint.multiplier
constant:cellConstraint.constant];
[self.contentView addConstraint:contentViewConstraint];
}
}

NSTextView background not resizing appropriately

I have an NSTextView inside an NSScrollView. The scroll view has the auto resize masks for height and width, so it changes size with the window it's in.
The text view is set up much in the way the apple documentation recommends here.
But no matter what settings I put on the text view, I cannot get the background color to resize along with the scroll view.
Here's a picture of what I'm dealing with:
The width works, but not the height.
Here's my setup code for the textview as it is for this picture:
NSTextView *view = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, scrollview.contentSize.width, scrollview.contentSize.height) textContainer:textContainer];
[view setMinSize:NSMakeSize(0.0, scrollview.contentSize.height)];
[view setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
[view setVerticallyResizable:YES];
[view setHorizontallyResizable:NO];
[view setAllowsDocumentBackgroundColorChange:YES];
[view setDrawsBackground:YES];
[view setAutomaticLinkDetectionEnabled:YES];
[view setAutoresizingMask:NSViewWidthSizable];
The white background colour isn’t that of the NSTextView, but rather that of its enclosing NSScrollView.
To change it, either select the NSScrollView in Interface Builder/Xcode 4. Or, to do it programmatically, use -[NSView encosingScrollView]:
NSScrollView *scrollView = [textView enclosingScrollView];
[scrollView setBackgroundColor:[NSColor blackColor]];

Resources