Vertical layout constraint splitting height evenly - objective-c

I am trying to build a fairly simple view.. I've got a horizontal toolbar (toolbarView) and a bottom accent line (bottomAccent) running the full width of the view. I also have two sidebars and a vertical accent next to that on the left side of the screen.
If I have one sidebar (comment out sidebarView2), life is great, it all looks just right. As soon as I add sidebarview2, I get these fun errors.. I'm not entirely sure why or what I'm doing wrong. I would expect my constraints to basically split the space for the sidebar in half, and fill each half with one of the sidebar views.. What am I missing here?
Unable to simultaneously satisfy constraints:
(
"<NSLayoutConstraint:0x7feefc3121e0 V:|-(8)-[NSTextField:0x7feefa4404e0] (Names: '|':BGView:0x7feefa440290 )>",
"<NSLayoutConstraint:0x7feefc312230 V:[NSTextField:0x7feefa4404e0]-(6)-[BGView:0x7feefa4408c0]>",
"<NSLayoutConstraint:0x7feefc3122b0 V:[BGView:0x7feefa4408c0(1)]>",
"<NSLayoutConstraint:0x7feefc312300 V:[BGView:0x7feefa4408c0]-(4)-[NSScrollView:0x7feefa440ba0]>",
"<NSLayoutConstraint:0x7feefc312350 V:[NSScrollView:0x7feefa440ba0]-(0)-| (Names: '|':BGView:0x7feefa440290 )>",
"<NSLayoutConstraint:0x7feefc313250 V:[BGView:0x7feefa440290]-(0)-[BGView:0x7feefa440290]>"
)
My code:
- (void)loadView {
NSView *rootView = [[NSView alloc] init];
NSView *toolbarView = _toolbarViewController.view;
[toolbarView setTranslatesAutoresizingMaskIntoConstraints:NO];
[rootView addSubview:toolbarView];
NSView *sidebarView = _titledScrollViewController.view;
[sidebarView setTranslatesAutoresizingMaskIntoConstraints:NO];
[rootView addSubview:sidebarView];
NSView *sidebarView2 = _titledScrollViewController.view;
[sidebarView2 setTranslatesAutoresizingMaskIntoConstraints:NO];
[rootView addSubview:sidebarView2];
BGView *horizontalSpacerSidebar = [[BGView alloc] initWithBackgroundColor:[NSColor colorWithDeviceRed:0.391 green:0.391 blue:0.43 alpha:1.0]];
[horizontalSpacerSidebar setTranslatesAutoresizingMaskIntoConstraints:NO];
[rootView addSubview:horizontalSpacerSidebar];
BGView *bottomAccent = [[BGView alloc] initWithBackgroundColor:[NSColor colorWithDeviceRed:0.223 green:0.77 blue:0.93 alpha:1.0]];
[bottomAccent setTranslatesAutoresizingMaskIntoConstraints:NO];
[rootView addSubview:bottomAccent];
NSDictionary *views = NSDictionaryOfVariableBindings(toolbarView, bottomAccent, sidebarView, sidebarView2, horizontalSpacerSidebar);
[rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[toolbarView]|" options:0 metrics:nil views:views]];
[rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sidebarView(==200)][horizontalSpacerSidebar(==1)]" options:0 metrics:nil views:views]];
[rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sidebarView2(==200)][horizontalSpacerSidebar(==1)]" options:0 metrics:nil views:views]];
[rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[bottomAccent]|" options:0 metrics:nil views:views]];
[rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[toolbarView(==53)][sidebarView][sidebarView2][bottomAccent(==3)]|" options:0 metrics:nil views:views]];
[rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[toolbarView(==53)][horizontalSpacerSidebar][bottomAccent(==3)]|" options:0 metrics:nil views:views]];
self.view = rootView;
}

Related

ConstraintsWithVisualFormat for iPhone X safe area

This line of code to be converted to safe area for iPhone X
self.view.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"carbonTabSwipe" : self.view,
#"topLayoutGuide" : rootViewController.topLayoutGuide,
#"bottomLayoutGuide" : rootViewController.bottomLayoutGuide
};
NSString *verticalFormat = #"V:[topLayoutGuide][carbonTabSwipe][bottomLayoutGuide]";
[rootViewController.view
addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalFormat
options:0
metrics:nil
views:views]];
[rootViewController.view
addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[carbonTabSwipe]|"
options:0
metrics:nil
views:views]];
This works for other devices but iPhone X it shows space at the bottom.

UILabel too large to display Ignoring bogus layer

I want to display a whole chapter of a book with UILabel in a scrollview. But seems the content is too large to display, and gives following error:
[2835:157182] -[<_UILabelContentLayer: 0x60400023b040> display]: Ignoring bogus layer size (378.666667, 883959.333333), contentsScale 3.000000, backing store size (1136.000000, 2651878.000000)
And I searched for answers and some guys suggest I should use a CATiledLayer to solve this, but they didn't give a clear clue of how to do this, anyone could give some help?
Thanks,
- (void)viewDidLoad {
[super viewDidLoad];
/*** Init the scrollview and the label ***/
UIScrollView *scrollView= [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scrollView];
UILabel *scrollViewLabel = [[UILabel alloc] init];
scrollViewLabel.numberOfLines = 0;
scrollViewLabel.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:scrollViewLabel];
scrollViewLabel.text = #"";
NSArray *array = [Utils getCharptersWithName:#"mybook"];
for(NSDictionary *dic in array){
scrollViewLabel.text = [scrollViewLabel.text stringByAppendingString:[dic objectForKey:#"content"]];
}
NSLog(#"%#", scrollViewLabel.text);
/*** Auto Layout ***/
NSDictionary *views = NSDictionaryOfVariableBindings(scrollView, scrollViewLabel);
NSArray *scrollViewLabelConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollViewLabel(scrollView)]" options:0 metrics:nil views:views];
[scrollView addConstraints:scrollViewLabelConstraints];
scrollViewLabelConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollViewLabel]|" options:0 metrics:nil views:views];
[scrollView addConstraints:scrollViewLabelConstraints];
NSArray *scrollViewConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[scrollView]-|" options:0 metrics:nil views:views];
[self.view addConstraints:scrollViewConstraints];
scrollViewConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[scrollView]-|" options:0 metrics:nil views:views];
[self.view addConstraints:scrollViewConstraints];
// Do any additional setup after loading the view.
}

Objective C Visual Autolayout NSTextField dynamic height

I am trying to create a dynamic size NSView using visual auto layout. I am trying to achieve something like the following diagram.
I added following constraints to achieve this.
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-15-[_iconImageView(39)]-12-[_textView(239)]-15-|"
options:NSLayoutFormatAlignAllTop metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-15-[_textView]-4-[_mainButton]-5-|"
options: NSLayoutFormatAlignAllLeft metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[_mainButton]-15-[_secondaryButton]"
options:NSLayoutFormatAlignAllTop metrics:nil views:views]];
But this creates the following layout.
As per my understanding, the following VFL will layout _textView 15px from top and then layout _mainButton after 4px from _textView bottom. But in actual _mainbottom is layout after 4px from top of _textView. Am i missing something here?
#"V:|-15-[_textView]-4-[_mainButton]-5-|"
Update
I replaced NSTextView with NSTextField. But now the problem is, NSTextField does not grow more than a single line height, but NSTextField is multi-line. Complete code for layouting and setting up views is as follow.
-(void)setupView {
_textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
[[_textField cell] setWraps:YES];
[_textField setLineBreakMode:NSLineBreakByWordWrapping];
[[_textField cell] setTitle:#"This is a _textView, This will contain dynamic resizing text."];
[_textField setSelectable:NO];
[_textField setEditable:NO];
[_textField setDelegate:self];
[_textField setBordered:NO];
[_textField sizeToFit];
[_textField setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:_textField];
_iconImageView = [[NSImageView alloc] initWithFrame:NSZeroRect];
[_iconImageView setImage:[NSImage imageNamed:#"notify-warning-icon"]];
[_iconImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:_iconImageView];
_mainButton = [[NSButton alloc] init];
_mainButton.translatesAutoresizingMaskIntoConstraints = NO;
[_mainButton setTitle:#"_mainButton"];
[self addSubview:_mainButton];
_secondaryButton = [[NSButton alloc] init];
_secondaryButton.translatesAutoresizingMaskIntoConstraints = NO;
[_secondaryButton setTitle:#"_secondaryButton"];
[self addSubview:_secondaryButton];
NSDictionary *views = NSDictionaryOfVariableBindings(_textField,_iconImageView,_mainButton,_secondaryButton);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-15-[_iconImageView(39)]-12-[_textField(239)]-15-|"
options:NSLayoutFormatAlignAllTop metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-15-[_textField]-4-[_mainButton]-5-|"
options: NSLayoutFormatAlignAllLeft metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[_mainButton]-15-[_secondaryButton]"
options:NSLayoutFormatAlignAllTop metrics:nil views:views]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:0.f constant:320.f]];
}
But if it changed this
#"V:|-15-[_textField]-4-[_mainButton]-5-|"
with this
#"V:|-15-[_textField(>=40)]-4-[_mainButton]-5-|"
I get the following output
But the problem is textfield content is dynamic and could change at runtime, it could be 2 lines, 3 lines etc. So how could i add some height constraint to NSTextField that will change NSTextField height depending on its content.
You can use a text field rather than a text view and set its preferredMaxLayoutWidth property.
By default, if preferredMaxLayoutWidth is 0, a text field will compute its intrinsic size as though its content were laid out in one long line (or, at least, without any maximum width). Even if you apply a constraint that limits its actual width, that doesn't change its intrinsic height and therefore it typically won't be tall enough to contain the text as wrapped.
If you set preferredMaxLayoutWidth, then the text field will compute its intrinsic size based on the text as wrapped to that width. That includes making its intrinsic height tall enough to fit.

How to use UIBezierPath in a auto layout view?

As far as I know, views displayed by constraints don't got a frame, so what should I do when I want to draw some lines in these views? Methods like moveToPoint do need a CGRect.
Here's my check: NSLog(#"%f,%f,%f,%f",self.contentView.frame.origin.x,self.contentView.frame.origin.y,self.contentView.frame.size.width,self.contentView.frame.size.height);
And the result is 0.000000,0.000000,0.000000,0.000000
For more details, here's my code:
-(void)loadView
{
self.view = [[UIView alloc]init];
self.titleView = [[UIView alloc]init];
self.placesHolder = [[UIView alloc]init];
self.contentView = [[UIView alloc]init];
self.titleView.translatesAutoresizingMaskIntoConstraints = NO;
self.placesHolder.translatesAutoresizingMaskIntoConstraints = NO;
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
self.titleView.backgroundColor = [UIColor grayColor];
self.placesHolder.backgroundColor = [UIColor blueColor];
self.contentView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.titleView];
[self.view addSubview:self.placesHolder];
[self.view addSubview:self.contentView];
NSDictionary *timeLineViewMap = #{#"titleView":self.titleView,
#"placesHolder":self.placesHolder,
#"contentView":self.contentView
};
NSArray *titleHorizon = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[titleView]|" options:0 metrics:nil views:timeLineViewMap];
NSArray *placesHolderHorizon = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[placesHolder(==58)]-0-[contentView]|" options:0 metrics:nil views:timeLineViewMap];
NSArray *titleVertical = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[titleView(==58)]-0-[placesHolder]|" options:0 metrics:nil views:timeLineViewMap];
NSArray *contentViewConstrain = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[titleView]-0-[contentView]|" options:0 metrics:nil views:timeLineViewMap];
[self.view addConstraints:titleHorizon];
[self.view addConstraints:placesHolderHorizon];
[self.view addConstraints:titleVertical];
[self.view addConstraints:contentViewConstrain];
}
Of course UIView does have a frame even with AutoLayout. You just don't set the values manually, AutoLayout is doing that for you. Methods like setFrame: are still called. Simply implement drawRect: like you always did.
Well, problem kind of solved, it seems that a frame of a view don't get a size until it's on the viewDidAppear move. Then I checked some other questions like iOS AutoLayout - get frame size width, which indicate that addConstraints should be put in the viewDidLayoutSubviews method, which is not in the viewController life cycle.

Aligning NSViews Using Autolayout Programmatically

I am having troubles aligning NSViews next to each other using Autolayout programmatically. To illustrate, I have made a very simple example project. DiscView is a subclass of NSView which draws a disc with a specified color.
In the example below, I only add Autolayout constraints on one of two subviews, and the result is as I expected (see screenshot below).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
DiscView *discViewA = [[DiscView alloc] init];
DiscView *discViewB = [[DiscView alloc] init];
discViewA.color = [NSColor blackColor];
discViewB.color = [NSColor redColor];
discViewA.translatesAutoresizingMaskIntoConstraints = NO;
discViewB.translatesAutoresizingMaskIntoConstraints = NO;
[self.customView addSubview:discViewA];
[self.customView addSubview:discViewB];
NSDictionary *discViewDictionary = #{#"discViewA" : discViewA,
#"discViewB" : discViewB};
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[discViewA]|" options:0
metrics:nil
views:discViewDictionary]];
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[discViewA]|" options:0
metrics:nil
views:discViewDictionary]];
}
However, if I try to show the two subviews side by side using some additional constraints, I only see a red disc. That is, it is as if the red disc is drawn directly on top of the black disc.
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[discViewA]-[discViewB]|" options:0
metrics:nil
views:discViewDictionary]];
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[discViewA]|" options:0
metrics:nil
views:discViewDictionary]];
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[discViewB]|" options:0
metrics:nil
views:discViewDictionary]];
What could possibly be wrong here?
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[discViewA][discViewB]|"
options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom
views:discViewDictionary]];
[self.customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[discViewA]|" options:0
metrics:nil
views:discViewDictionary]];
Try this. It might help. If not do what someone else suggest and put a constraint like so.
[self.customView addConstraint:[NSLayoutConstraint constraintWithItem:discViewA
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.customView
attribute:NSLayoutAttributeWidth
multiplier:0.5f
constant:0.f]];
My guess is that when you set both view to be side by side, AutoLayout does not know how to divide the width of the superview between your two views.
Try to add a width constraint to one of those subviews, it should be sufficient to resolve the ambiguity.