I have a PageViewController which I am programmatically adding to the current view via the following:
// Create the page view controller.
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homePageViewController"];
self.pageViewController.dataSource = self;
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
// Instantiate the first view controller.
UIViewController *startingViewController = [self viewControllerAtIndex:0];
[self.pageViewController setViewControllers:#[startingViewController]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:^(BOOL finished) {
// Completion code
}];
// Add the page view controller to this root view controller.
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
This is working well, however I need to add a constraint the this as I have an image view above and I want the top of the PageViewController to attach to the bottom of the ImageView. I have tried:
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.headerImage attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.pageViewController attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
and although the code builds fine, I get and error
Constraint items must each be an instance of UIView or subclass'
I've also tried:
NSDictionary *views = #{#"view": self.pageViewController.view,
#"top": self.headerImage };
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[top][view]" options:0 metrics:nil views:views]];
And although this build and runs without error, the constraint is not observed.
Any help greatly appreciated. Thanks
Related
I am working on a menubar app, that uses NSPopover. I am using the following code to present popover.
[self.mainPopover showRelativeToRect:[testView bounds] ofView:testView preferredEdge:NSMinYEdge];
Issue is this is being present too close to status bar as shown below.
Even if i change the rect it does not have any effect and rightly so, as the documentation states
The rectangle within positioningView relative to which the popover should be positioned. Normally set to the bounds of positioningView. May be an empty rectangle, which will default to the bounds of positioningView.
Following is the screenshot from dropbox app, was just wondering how can i add some spacing in my app like dropbox.
To achieve this i added a padding view and attach set NSStatusItem View to that container view. Code from the solution used is as follow for anybody looking to implement it.
_paddingView = [NSView new];
[_containerView addSubview:_paddingView];
[_containerView addSubview:_dragView];
[_dragView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_paddingView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_dragView(22)][_paddingView(5)]|" options:0
metrics:nil views:views]];
[_containerView addConstraint:[NSLayoutConstraint constraintWithItem:_dragView
attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
toItem:_paddingView attribute:NSLayoutAttributeLeft
multiplier:1. constant:0]];
[_containerView addConstraint:[NSLayoutConstraint constraintWithItem:_dragView
attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
toItem:_paddingView attribute:NSLayoutAttributeRight
multiplier:1. constant:0]];
self.mainPopover = [[NSPopover alloc] init];
self.mainPopover.delegate = self;
self.mainPopover.backgroundColor = [NSColor greenColor];
[self.mainPopover setAnimates:NO];
[self.mainPopover setBehavior:NSPopoverBehaviorTransient];
[self.mainPopover setContentViewController:viewController];
[_containerView layoutSubtreeIfNeeded];
[_statusItem setView:_containerView];
You could inset the test view bounds to add some margin between de view and popover:
NSPopover *mainPopover = [self mainPopover];
NSRect bounds = CGRectInset([testView bounds], -50.0, -50.0);
[mainPopover showRelativeToRect:bounds ofView:testView preferredEdge:NSMinYEdge];
I need a view controller without xib. There should be a webview with fully filled to the view. For that I've added the following code to loadView method.
- (void)loadView {
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
UIView *view = [[UIView alloc] initWithFrame:applicationFrame];
view.translatesAutoresizingMaskIntoConstraints = NO;
[view setBackgroundColor:[UIColor greenColor]];
[self setView:view];
// //create webview
self.webview = [[UIWebView alloc] initWithFrame:CGRectZero];
self.webview.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:self.webview];
[self.webview setBackgroundColor:[UIColor orangeColor]];
[self.webview setDelegate:self];
NSDictionary *viewBindings = NSDictionaryOfVariableBindings(view,_webview);
// //add constraints
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_webview]|" options:0 metrics:nil views:viewBindings]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_webview]|" options:0 metrics:nil views:viewBindings]];
}
But this turns the entire view to black. If I coment [view addConstraints:... method calls its showing a green view. What's wrong with my code?
I believe that the problem is that the parent view should not set translatesAutoresizingMaskIntoConstraints to false. The only view that must set that attribute to false is the one you are applying autolayout to, in this case webView. If you set view.translatesAutoresizingMaskIntoConstraints to false then you have to add constraints to view.
You don't need to change the root view of the UIViewController manually, maybe that's why it does not work.
My suggestion would be to try this out:
- (void) viewDidLoad {
[super viewDidLoad];
self.webview = [[UIWebView alloc] init];
self.webview.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:self.webview];
[self.webview setBackgroundColor:[UIColor orangeColor]];
[self.webview setDelegate:self];
NSDictionary *viewBindings = NSDictionaryOfVariableBindings(view,_webview);
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[_webview]-0-|" options:0 metrics:nil views:viewBindings]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[_webview]-0-|" options:0 metrics:nil views:viewBindings]];
// put a breakpoint after this line to see the frame of your UIWebView.
// It should be the same as the view
[self.view layoutIfNeeded];
}
This should work and your UIWebView should be full screen. Good luck!
How can I create this simple slide-in from the bottom view effect?
*
I have an action sheet method and i want to create this view controller effect that when i tap on one of the action sheet buttons i will have this view sliding in from the bottom of the screen, i dont need for you to explain to me how to add the date picker, only the effect of the view that sliding in and the background is darker when it slides and get liter when its dismissed
Please help me do that :))
this is my action sheet method where i want to perform this(instead of the modal):
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
NSLog(#"first button was pressed");
} else if (buttonIndex == 1) {
MyViewController *myViewController = [[MyViewController alloc]initWithNibName:#"MyViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:myViewController];
[self.navigationController presentViewController:navigationController animated:YES completion:nil];
}
}
Here's an autolayout approach:
Create a "cover view" with a dark transparent background and constrain it to fill the window. Insert it above all other views.
Create the view containing the date picker and add it to the window above the cover view with a top constraint to the bottom of the window.
Call layoutIfNeeded() on the date picker view to trigger auto layout. Now the view is positioned "below" the screen and not yet visible to the user.
Remove the top constraint and add a bottom constraint to the bottom of the window.
Call layoutIfNeeded() again inside an animation block and the view will slide up from the bottom of the screen to its final position. You can also fade in the cover view within the animation block.
Here's a quick example. I haven't tested this exact code, but I've done this kind of thing many times and you should be able to get something from this approach I hope! Please note this assumes the MyActionSheet has a valid intrinsicContentSize.
// Create cover view
coverView = [UIView new];
coverView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.4];
coverView.alpha = 0.0;
// Add to window
[window addSubview:coverView];
[window bringSubviewToFront:coverView];
// Pin to edges
[window addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[coverView]|" options:kNilOptions metrics:nil views:NSDictionaryOfVariableBindings(coverView)]];
[window addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[coverView]|" options:kNilOptions metrics:nil views:NSDictionaryOfVariableBindings(coverView)]];
// Create action sheet
MyActionSheet *sheet = [MyActionSheet new];
[window insertSubview:sheet aboveSubview:coverView];
// Add horizontal constraints
[window addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sheet]|" options:kNilOptions metrics:nil views:NSDictionaryOfVariableBindings(sheet)]];
// Pin to starting position
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:sheet attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:window attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
[window addConstraint:topConstraint];
// Layout to start
[sheet layoutIfNeeded];
// Pin to final position
[window removeConstraint:topConstraint];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:sheet attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:window attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
[window addConstraint:bottomConstraint];
// Animate to final position
[UIView animateWithDuration:0.3 animations:^{
coverView.alpha = 1.0;
[sheet layoutIfNeeded];
}];
If you don't want to use auto layout for some reason, you can manually adjust the frames of the views provided you can calculate the height of the action sheet.
In my application, I have :
The main Window, containing a "custom view", which is an NSView
dropped in IB.
A View Controller + an other nib, containing a view and some
controls.
When the app loads, I am using :
initWithNibName:nibName andReplaceView:(the custom view) resize:YES
To replace the custom view. I know there is an option placeholder for the views in IB, but I do not know how to use it, and my app works well this way...
... except that the loaded view does not inherit the layout constraints of the replaced view.
How can I fix this ?
Edit: Sorry, I forgot that the function was mine... I wrote it a long time ago in a category. Here is the code :
- (id)initWithNibName:(NSString*)nibName andReplaceView:(NSView*)aView resize:(BOOL)resize
{
// 1. Loading the bundle
if (self = [self initWithNibName:nibName bundle:nil])
{
[self replaceView:aView resize:resize];
}
return self;
}
- (void)replaceView:(NSView*)aView resize:(BOOL)resize
{
if (resize)
{
NSRect insertionFrame = [aView frame];
[[self view] setFrame:insertionFrame];
}
else
{
NSRect insertionFrame = [aView frame];
insertionFrame.size.width = [[self view] frame].size.width;
insertionFrame.size.height = [[self view] frame].size.height;
[[self view] setFrame:insertionFrame];
}
NSView* supView = [aView superview];
[supView replaceSubview:aView with:[self view]];
}
When you replace a view, it removes all the layout constraints attached to the old view.
Personally, I just put the new view inside the old view. Here is some code I've used:
#implementation SJPlaceholderView
-(void) fillWithView:(NSView*)view {
NSParameterAssert(view);
view.frame = self.bounds;
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:view];
[self addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[view]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(view)]];
[self addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(view)]];
}
#end
This makes sure the inner view frame matches the outer view frame exactly. All the layout constraints on the outer view are still in effect.
You could also try to loop through all the constraints of the old view, and apply them to the new view. Most of the constraints will be on the view itself, or the superview, but they could theoretically be on any of the ancestor views.
I'm animating a custom view off screen using a constraint on view's top edge.
The view is:
#implementation MyView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setBackgroundColor:[UIColor yellowColor]];
_label = [UILabel new];
[_label setTranslatesAutoresizingMaskIntoConstraints:NO];
_label.text = #"text";
[self addSubview:_label];
[self updateConstraints];
}
return self;
}
- (void)updateConstraints
{
NSDictionary *views = NSDictionaryOfVariableBindings(_label);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[_label]"
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label]"
options:NSLayoutFormatAlignAllCenterY
metrics:nil
views:views]];
[super updateConstraints];
}
#end
The constraint for animating myView on its parent view is:
_myViewTopConstraint = [NSLayoutConstraint constraintWithItem:_myView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-kMyViewVisibleHeight];
[self.view addConstraint:_myViewTopConstraint];
The animation on the top edge constraint is:
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.myViewTopConstraint.constant = kScreenHeight;
[self.myView layoutIfNeeded];
} completion:nil];
When the label and constraints in MyView is commented out, the animation works perfectly. But when the label and constraints are there, the animation is sped up significantly, maybe 5x or more.
Why does this happen? Anyone know how to fix it?
Figured it out. layoutIfNeeded must be called on myView's parent view in the animation block.
[self.myView layoutIfNeeded];
changed to
[self.view layoutIfNeeded];