ConstraintsWithVisualFormat for iPhone X safe area - objective-c

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.

Related

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.
}

How do I simply programmatically place a UIButton upon a host view via Constraints?

I want to programmatically generate an overview (UIButton) in the lower left-hand corner.
This is currently being done using Interface Builder. However I need to repeat this for multiple host UIViews. So I decided to write a utility class method to do this.
+ (UIButton *)attachGreenPlusButtonTo:(UIView *)hostView {
NSLog(#"--- {attachGreenPlusButtonTo} ---");
// Green Button --------------------------------------------------------------------
UIImage *greenPlusImage = [UIImage imageNamed:#"btn_plus"];
UIButton *greenButton = [UIButton buttonWithType:UIButtonTypeCustom];
[greenButton setImage:greenPlusImage forState:UIControlStateNormal];
greenButton.tag = 22;
[greenButton setTranslatesAutoresizingMaskIntoConstraints:NO];
[hostView addSubview:greenButton];
// Positioning the Green '+' Button:
// Button is 4 pts from superview right edge:
NSArray *horizontalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[greenButton(==57)]-4-[hostView]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(hostView,greenButton)];
[hostView addConstraints:horizontalConstraints];
NSArray *verticalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat: #"V:[greenButton(==57)]-34-[hostView]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(hostView,greenButton)];
[hostView addConstraints:verticalConstraints];
[hostView layoutIfNeeded];
return greenButton;
}
I'm getting close, but now the button is being positioned at (-61, -91).
What gives?
Call this after creating the greenButton:
[greenButton setTranslatesAutoresizingMaskIntoConstraints:NO];
otherwise the system will create (unwanted) constraints for you.
Your visual layout doesn't look right either. Use vertical bar | to refer to the superview:
"H:[greenButton(==57)]-4-|"
and
"V:[greenButton]-34-|"
I thought I had a problem using '|' for superview within the method due to a bug which eventually appeared to be due do something else. So...
Upon feedback, I return to the use of '|' as the superview vs the 'hostView':
// Positioning the Green '+' Button:
// Button is 4 pts from superview right edge:
NSArray *horizontalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[greenButton(==57)]-4-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(hostView,greenButton)];
[hostView addConstraints:horizontalConstraints];
NSArray *verticalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat: #"V:[greenButton(==57)]-34-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(hostView,greenButton)];
[hostView addConstraints:verticalConstraints];
[hostView layoutIfNeeded];
This worked.
All appears to be okay now.

Vertical layout constraint splitting height evenly

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;
}

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.

Programatic NSLayoutConstraints in a single string

Is there a way to combine below 3 syntax lines into a single one statement like (which doesn't work):
[_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[cardContainer1]|[cardContainer2]|[cardContainer3]|" options:0 metrics:nil views:viewsDictionary]];
1. [_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[cardContainer1]|" options:0 metrics:nil views:viewsDictionary]];
2. [_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[cardContainer2]|" options:0 metrics:nil views:viewsDictionary]];
3. [_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[cardContainer3]|" options:0 metrics:nil views:viewsDictionary]];
No, but you can do it in two lines:
[_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[cardContainer1]|" options:0 metrics:nil views:viewsDictionary]];
This pins card container 1 to the top and bottom of the superview. Then:
[_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[cardContainer1][cardContainer2][cardContainer3]|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:nil views:viewsDictionary]];
Which aligns the top and bottom of the remaining card containers.
This assumes you want the views laid out next to each other, you didn't actually specify any horizontal layout details.
It helps when using the visual format to think about each line of visual format language representing a single row or column of layout in your view.
To the best of my knowledge, no.
It's not really a single line, but you can reduce the duplication of your three lines by doing them in a loop:
for (NSView *view in #[cardContainer1, cardContainer2, cardContainer3]) {
[_cardsContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
}