This has been dogging me a bit.
Basically, what it comes down to is that when I manually define autolayout constraints using layout anchors, and use the view controller in a popover, the center is in the wrong place. It's seeming to center around the boundary from the left of the popover pop-out arrow to the left and the right margin, which is not the center of the area of the popover.
This seems to work fine with other UIViewController presentation methods, like UIModalPresentationFormSheet.
Any ideas how I can fix this in a layout-friendly way without special-casing the margins?
PS. The exact same thing happens if you peg the layout to the center of the parent view, it's slightly off.
(note: Edited because I posted the source badly)
-(void)loadView {
// create root view.
UIView *view = [[UIView alloc] init];
[view setBackgroundColor:[UIColor greenColor]];
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.view = view;
UIView *parentView = [[UIView alloc] init];
[view addSubview:parentView];
[parentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[parentView setBackgroundColor:[UIColor redColor]];
[parentView.leadingAnchor constraintEqualToAnchor:view.leadingAnchor constant:20.0].active = YES;
[parentView.trailingAnchor constraintEqualToAnchor:view.trailingAnchor constant:-20.0].active = YES;
[parentView.topAnchor constraintEqualToAnchor:view.topAnchor constant:10.0].active = YES;
[parentView.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:-10.0].active = YES;
}
The edges of the view are not where you think they are; the leading edge is where the point of the arrow is, not where the side of the visible rectangle is.
However, the margins are where you expect them. So pin to the layoutMarginsGuide instead, and all will be well. Indeed, this is one of the things margins are intended to cover.
In Swift:
parentView.leadingAnchor.constraint(
equalTo: self.view.layoutMarginsGuide.leadingAnchor, constant:0).isActive = true
parentView.trailingAnchor.constraint(
equalTo: self.view.layoutMarginsGuide.trailingAnchor, constant:0).isActive = true
(And change those constant numbers as you see fit.)
Related
I have been working with constraints in Swift for iOS, and the documentation does a good job of explaining things.
However, one thing that has recently confused me is the difference between view.topAnchor and view.safeAreaLayoutGuide.topAnchor.
When I have set constraints programmatically in my iOS app, I have found that I can use these totally interchangeably, so I can't tell what the difference between the two is.
I checked out the documentation at developer.apple.com, and I haven't found anything explaining the differences.
Is there any difference between the two properties?
The main difference is with devices that has top notch screens as well as home button like iphone 11 inside your view other than physical button like iphone 8 and older. also as it says it keeps you in the safe area even while device in landscape rotation, however some designs require using top anchor.
UIView.topAnchor represents the top edge of the view's frame.
UIView.safeLayoutGuide is a rectangle area representing the view's safe area, the portion of the view that is unobscured by bars and other content.
UIView.safeLayoutGuide.topAnchor is the top edge of UIView.safeLayoutGuide.
Example:
Try create an empty storyboard project in Xcode, modify "ViewController.m" with the code below. This code create red topView and bottomView that is inside the parent view, but outside of the view's safeArea.
#import "ViewController.h"
#interface ViewController()
#property UIView* topView;
#property UIView* bottomView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.topView = [[UIView alloc] init];
[self.topView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.topView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.topView];
[NSLayoutConstraint activateConstraints:#[
[self.topView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[self.topView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.topView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
]];
self.bottomView = [[UIView alloc] init];
[self.bottomView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.bottomView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.bottomView];
[NSLayoutConstraint activateConstraints:#[
[self.bottomView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
[self.bottomView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[self.bottomView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor]
]];
}
Some screenshots of the exmaple: (Please ignore the button)
I added a UISegmentedControl programmatically to navigationItem.titleView
The first time it is displayed it is centered like I would expect. But as soon as I dive down into a detail view controller, I can see my titleView's view pushed into the upper left corner, and nothing I've tried gets it back
In my initialization method I do this
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:segmentTextContent];
segmentedControl.selectedSegmentIndex = 0;
[segmentedControl setTranslatesAutoresizingMaskIntoConstraints:NO];
segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
//segmentedControl.frame = CGRectMake(0, 0, 280.0f, 30.0f);
self.navigationItem.titleView = segmentedControl;
I realized after a while that setting the CGRectMake has no affect
I've also tried putting this login in viewDidAppear but also did not change this outcome
Previously, when I tried putting a UISegmentedControl inside of another UIView, I lost the ability to click on the UISegmentedControl (I'm not sure if I had a variation of this which solved my positioning problems though)
If you only have 2 segments I don't think it's needed for the resizing, its width will always fit even for iPhone 4 and lower.
What happens if you remove the following:
[segmentedControl setTranslatesAutoresizingMaskIntoConstraints:NO];
segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
I created a UIView programmatically in viewDidLoad and simply want to center it in self.view. With the iPhone 5, the view is correctly centered. When I switch to the 3.5 simulator my view is pushed towards the bottom of self.view.
What in the world could I be doing wrong? My xib has nothing but a empty view inside, and even if I create my view not specifying a xib I get the same result.
How do I simply center my new UIView in self.view without jumping through a million hoops?
EDIT:
My code is as simple as it gets:
UIView *newView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width)];
newView.backgroundColor = [UIColor redColor];
[self.view addSubview:newView];
newView.center = self.view.center;
I'm not sure why my question is being downvoted as it's pretty straightforward what I'm trying to do. If there's something missing please let me know so I can adjust my question accordingly.
First, you need to tell us if your app is using auto layout or struts and springs. The answer is completely different depending on which you're using.
If you're using auto layout then the center position is pretty much ignored and what matters is the constraints you put on the view.
If you're not using auto layout, the code above isn't right. The center property of view is in the coordinate system of the superview.
Your code to set the center should look like this:
CGPoint center = [self.view convertPoint self.view.center fromView: self.view.superview];
newView.center = center;
But as I say, if you're using auto layout then that won't make any difference.
You can use autosizing with this code
UIView *view=[[UIView alloc] init];
view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
This will give
So the view will stay where you add it and will not change while the size of the device changes.
I am using the following code to create a view and put it on top:
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
CGRect viewRect = mainWindow.frame;
topView = [[UIView alloc] initWithFrame:viewRect];
[topView setBackgroundColor:[UIColor colorWithWhite:0.2 alpha:0.4]];
[mainWindow addSubview:topView];
It works perfectly but my problem is if I write anything one the view(like using a Label) and my device is in a landscape position, the text is in vertical position. I've attached a picture to make it more clear. Is there any way to fix it?
EDIT: if I use UIApplication.sharedApplication.keyWindow.rootViewController instead of mainWindow, I will get this:
See this answer:
View on top of everything: UIWindow subview VS UIViewController subview
Basically, only the first subview of the main window gets rotation events, so you have to do it some other way.
There's no way to fix it without adding the given view to a View Controller (UIWindow was never meant to handle rotation, and has no logic to do so).
The wonky view rotation results you're experiencing are actually the result of UIView's default autoresizingMask's
topView = [[UIView alloc] initWithFrame:viewRect];
[topView setBackgroundColor:[UIColor colorWithWhite:0.2 alpha:0.4]];
[topViewsetAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
I am trying to setup a view programatically. Through preference I prefer to programme the views as opposed to using Interface Builder, I feel I have a better control for some reason...
I am setting up a view with two subviews and a button. What I am trying to acheive is the same view when the orientation changes. Initially I thought I needed to calculate the screen size then calculate some divisions to work out the changes, but it appears I can handle on UIViewAutoresizing***
The issue I experience is with the top margin. Here is my code for the subviews.
// Create sub view for Logo
UIView *logoView =[[UIView alloc] initWithFrame:CGRectMake(0,0,320,280)];
[logoView setBackgroundColor:[UIColor blueColor]];
logoView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
// Create sub view
UIView *buttonView =[[UIView alloc] initWithFrame:CGRectMake(0, logoView.bounds.size.height, 320,200)];
[buttonView setBackgroundColor:[UIColor redColor]];
buttonView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin;
Here is a picture of portrait and landscape where you can see the issue, with the 'white' space.
You either want the two views (red and blue) to end up with a proportionate amount of space after the rotation:
logoView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleBottomMargin;
buttonView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin;
Or you want the red view to end up the same size as it started out, and the blue view to adjust to make room for it:
logoView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
buttonView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin;
use autolayouts . U dont have to deal with any of these issues ...