Set PKPaymentButton width to superview? - cocoa-touch

I'm adding a PKPaymentButton programmatically (because it doesn't seem like I can simply set a UIButton to PKPaymentButton in storyboard). I'd like my PKPaymentButton to be width of its superview. However, this does not seem to be working. (The PKPaymentButton maintains a predefined width of 140pts.) I've tried setting the horizontal contentHuggingPriority of my PKPaymentButton to UILayoutPriorityDefaultLow but that seems to have no effect. Any ideas how to get a full-width PKPaymentButton that adjusts to the width of my screen?

I've created container view in storyboard, and then added to it PKPaymentButton with necessary constraints.
Here is simple method from my UIView category that makes it easy:
-(void) setupWithContentView:(UIView *) contentView
{
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:contentView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[contentView]|"
options:0
metrics:nil
views:#{#"contentView":contentView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:#{#"contentView":contentView}]];
}

Related

How to set constraints on multi line labels within a tableview cell

I have 4 custom labels inside a custom Tableview cell. The tabelview cell's height is a fixed number.
I am setting the auto layout constraints of the labels programatically.
I've allowed the labels to be as tall as the content inside the labels (NumberOfLines=0).
I've set the width constraints correctly and I've also set the padding constraints inside a loop like so:
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousLabel]-20-[customLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customLabel, previousLabel)]];
Everything is laid out correctly except when one OR more of the labels have too much content and they overflow into the next cell.
I want to programatically add constraints (I think it might be compression resistance and content hugging values) so that the labels stop at the end of their tableview cell.
====
Here is the code where I setup some of the constraints:
for (UIView *subView in cell.contentView.subviews) {
if ([subView isKindOfClass:[AFRCustomLabel class]]) {
customLabel = (AFRCustomLabel *)subView;
if (!previousLabel) {
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-40-[customLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customLabel)]];
} else {
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousLabel]-20-[customLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customLabel, previousLabel)]];
}
previousLabel = customLabel;
[cell.contentView addConstraints:constraints];
}
}
Here is a screen shot of the label's text overflowing. Keep in mind 2 labels could overflow.

AutoLayout container relative position not recognised under ios8 beta 4

In my view, I init a view like this:
UIView *statsView = [[UIView alloc] initWithFrame:CGRectMake(10, 130, 200, 12)];
Inside this view, I create 3 autolayout subviews contained in this previous view.
for (UIButton* v in [statsView subviews]) {
v.translatesAutoresizingMaskIntoConstraints = NO;
[statsView addSubview:v];
}
NSDictionary *views = #{#"reposts": v1,
#"likes": v2,
#"comments": v3
};
[statsView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[reposts][likes][comments]"
options:NSLayoutFormatAlignAllTop| NSLayoutFormatAlignAllBottom
metrics:0
views:views]];
On IOS7 , the Y position of the container is 130 (defined in the initWithFrame), but with ios8, the Y position looks like "0".
Any ideas?
I ask an other question about the same problem and Saldorino give me a good answer
Xcode6 - Autolayout view in an other autolayout view
The issue lies in the missing the vertical position constraint.
[statsView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[comments]"
options:0
metrics:0
views:views]];

NSLayoutConstraint behaving differently when drawing to NSBitmapImageRep

I have this control called ITNavigationView on Github.
It's smoothly animating from one NSView to another by caching and adding them to a NSImageView.
When caching the view, a subview centred in the x axis will be pulled exactly 1 pixel to the right.
If I instead add a leading constraint, this doesn't happen.
How can I prevent this from happening?
To cache the view, I'm using this code:
- (NSImage *)imageOfView:(NSView *)view {
[view layoutSubtreeIfNeeded];
[view setNeedsUpdateConstraints:YES];
[view updateConstraintsForSubtreeIfNeeded];
NSBitmapImageRep* rep = [view bitmapImageRepForCachingDisplayInRect:view.bounds];
[view cacheDisplayInRect:view.bounds toBitmapImageRep:rep];
return [[NSImage alloc] initWithCGImage:[rep CGImage] size:view.bounds.size];
}
EDIT
Also worth noting is that this only happens when the superview has an odd width.

UIStoryboard how to replace constraints programmatically?

I have a view controller laid out in a storyboard with autolayout enabled and am looking for a way to change constraints to allow for my view to rotate into landscape and rearrange buttons on the screen. When I try the code below, I get about two dozen "unable to satisfy constraints, breaking constraint..." messages that I cannot really decode.
Is there a way to dynamically replace constraints from a storyboard with constraints that I specify programmatically? I want to completely override the layout of the buttons that I defined in a storyboard.
-(void)updateViewConstraints
{
[super updateViewConstraints];
self.loginButton.translatesAutoresizingMaskIntoConstraints = NO;
self.getStartedButton.translatesAutoresizingMaskIntoConstraints = NO;
self.takeTourButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.loginButton removeConstraints:self.loginButton.constraints];
[self.getStartedButton removeConstraints:self.getStartedButton.constraints];
[self.takeTourButton removeConstraints:self.takeTourButton.constraints];
one = self.loginButton;
two = self.getStartedButton;
three = self.takeTourButton;
NSDictionary *metrics = #{#"height":#50.0,#"width":#100.0,#"leftSpacing":#110.0};
NSDictionary *views = NSDictionaryOfVariableBindings(one,two,three);
[self.view removeConstraints:activeTestLabelConstraints];
[activeTestLabelConstraints removeAllObjects];
if(isRotatingToLandscape)
{
[self registerConstraintsWithVisualFormat:#"|-[one(two)]-[two(three)]-[three]-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"V:[one(height)]-|" options:0 metrics:metrics views:views];
}else
{
[self registerConstraintsWithVisualFormat:#"|-leftSpacing-[one(width)]" options:0 metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"[two(width)]" options:0 metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"[three(width)]" options:0 metrics:metrics views:views];
[self registerConstraintsWithVisualFormat:#"V:[one(height)]-[two(75)]-[three(100)]-|" options:NSLayoutFormatAlignAllCenterX metrics:metrics views:views];
}
}
Updated with Rob's answer, here's the method to remove constraints that I use
-(void)removeConstraintForView:(UIView*)viewToModify
{
UIView* temp = viewToModify;
[temp removeFromSuperview];
[self.view addSubview:temp];
}
It sounds like you want to just remove all the constraints on a view. Since constraints on a view are often held by an ancestor of the view, it's not obvious how to remove all of the constraints easily. But it is actually pretty easy.
Removing a view from the view hierarchy removes any constraints betwixt the view and other views outside of it. So just remove your view from its superview and then add it back:
// Remove constraints betwixt someView and non-descendants of someView.
UIView *superview = someView.superview;
[someView removeFromSuperview];
[superview addSubview:someView];
If there are any constraints betwixt someView and its descendants, those constraints might be held by someView itself (but cannot be held by any descendants of someView). If you want to remove those constraints also, you can do so directly:
// Remove any remaining constraints betwixt someView and its descendants.
[someView removeConstraints:[someView constraints]];

UIViewController viewDidLoad

I've got a new project and I can work with iOS 5+ feature, so I choose to use both AutoLayout and UIViewController childControllers capabilities.
The screen I'm trying to create is simple :
1. The top area has lot of "complex" controls.
2. The bottom area is a tableview to visualize results.
What I want
I want the controls area to take all necessary place (ie the place I gave into Interface Builder). The TableView will shrink as needed.
Problem
When loaded from a XIB the UIViewController.view frame has ALWAYS a size of 0x568. I was expected 0x200 (if 200 was the size I configured in Interface Builder).
What works : with AutoLayout
I created a XIB with all my element. I did the outlet on the "root view" and I did outlet and all views containers.
Then in my viewDidLoad method I added the containers and configured constraints.
-(void) setupConstraints
{
NSMutableArray *constraints = [NSMutableArray arrayWithCapacity:1];
// Views dictionary.
UIView *topView = self.topView;
UIView *table = self.tableTracks;
NSDictionary *views = NSDictionaryOfVariableBindings(topView, table);
// Views metrics.
NSNumber *topViewHeight = [NSNumber numberWithFloat:self.topView.frame.size.height];
NSDictionary *metrics = NSDictionaryOfVariableBindings(topViewHeight);
// Pin the topView to the top edge of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topView(==topViewHeight)][table]|" options:0 metrics:metrics views:views]];
// Pin the topView edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topView]|" options:0 metrics:nil views:views]];
// Pin the table edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[table]|" options:0 metrics:nil views:views]];
[self.view addConstraints:constraints];
}
With this, I can simple resize the topView view with Interface Builder and the TableView will be resized as needed (no compression resistance, I don't care if we can't see the table).
What DO NOT works : with AutoLayout and ChildControllers
Then for simplicity I choose to use ChildControllers (lot of outlet required to perform custom UIFont setup, too bad XCode can't handle them yet!).
I made the following modifications :
Created HomeTopViewController classes + XIB. Created HomeTableViewController classes + XIB.
Copied all views from the origin to the correct XIB. Add the UIViewController references + wired the outlets.
The root container of HomeTopViewController is configured to 200px height.
Wired my containers to the view outlet of my childs controllers.
Then I updated my setup code to the following :
-(void) _addChildViewControllersAndSetupConstraints {
// Get required metrics variables
NSNumber *topViewHeight = [NSNumber numberWithFloat:self.topViewController.view.frame.size.height];
CFShow(self.topViewController.view);
// log is : <UIView: 0xa209c20; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0xa2097e0>>
NSLog(#"self.topViewController.view.frame.size.height = %f", self.topViewController.view.frame.size.height);
// Informs controllers the ADD operation started
[self addChildViewController:self.topViewController];
[self addChildViewController:self.tableViewController];
// Add view to the view's hierarchy
self.topViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
self.tableViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.topViewController.view];
[self.view addSubview:self.tableViewController.view];
// Setup constraints
NSMutableArray *constraints = [NSMutableArray arrayWithCapacity:1];
// Views dictionary
UIView *topView = self.topViewController.view;
UIView *table = self.tableViewController.view;
NSDictionary *views = NSDictionaryOfVariableBindings(topView, table);
// Views metrics dictionary
NSDictionary *metrics = NSDictionaryOfVariableBindings(topViewHeight);
// Pin the topView to the top edge of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topView(==topViewHeight)][table]|" options:0 metrics:metrics views:views]];
// Pin the topView edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topView]|" options:0 metrics:nil views:views]];
// Pin the table edges to both sides of the container.
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[table]|" options:0 metrics:nil views:views]];
// Adds constraints
[self.view addConstraints:constraints];
// Informs controllers the ADD operation ended
[self.topViewController didMoveToParentViewController:self];
[self.tableViewController didMoveToParentViewController:self];
}
Problem
No matter how I size the UIView container in HomeTopViewController the line CFShow(self.topViewController.view); will always give me frame = (0 0; 320 568) when I expected 200px.
I did configure the layout size to "Freeform" or "None".
What I want to avoid as much as possible
I would hate having to create other outlets then View on HomeTopViewController and HomeTableViewController and storing the initial frame / height into a UIViewController property.
Questions
At load time, are all controller.view.frame property sized on the screen ? (be it 3,5 or 4 inches)
How can I solve this without hardcoding the size somewhere // creating additionnal outlets ?
This would be easier if you used a storyboard. You can add container views to you main controller's view and size them how you like, and you automatically get view controllers embedded in these container views that are set to the correct size. These container views (next to the regular UIView in the object list) are only available from a storyboard, not xibs. This will require almost no code, and you probably won't have to add any constraints manually.