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.
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 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
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 have an UIScrollview which will contain X views, each view is of Class type "CategoryPostView" that contains an UIImageView. I am adding these views on the scrollView one after another. But when the image loads in the imageView I want to change the size of my imageView, therefor I want to change the Offset y of my all other views in the scrollView.
I am using autolayout constraint on UIScrollView but I couldn't reach to any result. This is the code I am using:
int categoryScrollY = 0.0;
CategoryPostView *previousView = nil;
for(; self.indexOfCate
goryInTheScroll < [self.categoryItemsArray count]; self.indexOfCategoryInTheScroll++){
PostItem *postItemObj = [self.categoryItemsArray objectAtIndex:self.indexOfCategoryInTheScroll];
CategoryPostView *categoryPostViewObj = [[CategoryPostView alloc] initWithFrame:CGRectMake(0.0, categoryScrollY, 148.0, 76.0)];
categoryPostViewObj.translatesAutoresizingMaskIntoConstraints = NO; //This part hung me up
[categoryPostViewObj setupPostViewWithItem:postItemObj];
[self.categoriesScrollView addSubview:categoryPostViewObj];
categoryScrollY += 80.0;
[_categoriesScrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[categoryPostViewObj]"
options:0 metrics:nil
views:#{#"categoryPostViewObj":categoryPostViewObj}]];
if (!previousView) { // first one, pin to top
[self.categoriesScrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[categoryPostViewObj]"
options:0 metrics:nil
views:#{#"categoryPostViewObj":categoryPostViewObj}]];
}else{
[self.categoriesScrollView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[previousView][categoryPostViewObj]"
options:0 metrics:nil
views:#{#"categoryPostViewObj":categoryPostViewObj, #"previousView":previousView}]];
}
if(self.indexOfCategoryInTheScroll == ([self.categoryItemsArray count] - 1))
{
[self.categoriesScrollView addConstraint:[NSLayoutConstraint constraintWithItem:categoryPostViewObj
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.categoriesScrollView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0]];
}
previousView = categoryPostViewObj;
}
For now, I have the views on top of each other. I have to somehow apply my constraint from the bottom of the previous view and not from the top of the previous view. I guess I should add options to this line of code:
[self.categoriesScrollView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[previousView][categoryPostViewObj]"
options:0 metrics:nil
views:#{#"categoryPostViewObj":categoryPostViewObj, #"previousView":previousView}]];
Many thanks!
The best way to use constraints inside a UIScrollView is to not use them at all.
If you want to layout the inside of the scroll view using constraints then create a UIView to contain everything in the scroll view and then place this view into the scroll view.
By doing this you can now use the container view that you created to place all the constraints inside.
I ended up using the class UIView+AutoLayout.
You can find it here https://github.com/smileyborg/UIView-AutoLayout and https://github.com/jrturton/UIView-Autolayout
In my main view, I do some gesture action causing some new view to be shown. At this time I want to dim the entire background (except this new view) as a good UI practice. In HTML, Javascript it looks like so - How do I get this same effect in iOS ?
Lay a UIView over the background, set its background to [UIColor blackColor] and set its opacity to like 0.6. Then add that UIView to the parent view.
This'll dim out the background AND intercept any taps to background controls.
While Dan's suggestion is on target, you cannot use a modal view controller in this case because a typical modal covers the entire screen, blocking the parent view controller, even if you have the background of your view as transparent (you'll see whatever you have in the application's UIWindow).
If you are doing this on the iPad there are 2 modal presentation styles (UIViewModalPresentationStylePageSheet and UIViewModalPresentationStyleFormSheet) that can do something similar, but those will not work on an iPhone or iPod Touch.
Add the "shadow" view, with the dark background and partial opacity and whatever view you want to be in the foreground, to the view controller's view directly. You can animate them in using standard UIView animation blocks, or CoreAnimation.
Another note, if you want to intercept touches to that shadow view you can either make it a giant button, subclass UIView to override one of the touch methods like touchesEnded: or change it to a UIControl which can receive touch events like a UIButton, but without the extra options for text, shadows, states etc.
The above two answers work if the new view has it's own 'background', i.e. has no transparency. The problem that led me here cannot be solved that way though, what I was trying to do is this: I'm running an AVCaptureSession on my screen with the usual AVCaptureVideoPreviewLayer to display video in real time. I want to select a part of the screen (to later do something with only this part), and when this part is selected want to dim the rest of the video preview. This is what it looks like:
This is how I solved this: A new view is created depending on where the screen is touched and added as a subview of the video preview view. Then, I create 4 additional, non-overlapping rectangular subviews with the before-mentioned background color and opacity to cover the rest of the screen. I explicitly need all these views not to be subviews of the main view, because I need the ability to touch the screen somewhere else and change the selected area.
I'm sure there are more elegant ways to solve this, so if someone knows how please comment...
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UIGestureRecognizerDelegate>
{
UIView *mainView;
UIView *dimBackgroundUIView;
UIView *customView;
UIButton *btn;
}
#end
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
mainView = [[UIView alloc] init];
mainView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:mainView];
[self addConstraintsForMainView];
btn = [[UIButton alloc] initWithFrame:CGRectMake(200, 200, 100, 30)];
[btn setTitle:#"Button" forState:UIControlStateNormal];
[btn setBackgroundColor:[UIColor blueColor]];
[btn addTarget:self action:#selector(showCustomView_Action:) forControlEvents:UIControlEventTouchUpInside];
[mainView addSubview:btn];
dimBackgroundUIView = [[UIView alloc] init];
dimBackgroundUIView.backgroundColor = [UIColor blackColor];
dimBackgroundUIView.alpha = 0.2;
}
-(void)removeCustomViewsFromSuperView {
if(dimBackgroundUIView)
[dimBackgroundUIView removeFromSuperview];
if(customView)
[customView removeFromSuperview];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)showCustomView_Action:(id)sender {
customView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 80, 80)];
customView.backgroundColor = [UIColor redColor];
[self.view addSubview:dimBackgroundUIView];
[self addConstraintsForDimView];
[self.view addSubview:customView];
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(removeCustomViewsFromSuperView)];
tapped.delegate=self;
tapped.numberOfTapsRequired = 1;
[customView addGestureRecognizer:tapped];
}
-(void) addConstraintsForDimView {
[dimBackgroundUIView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:dimBackgroundUIView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:dimBackgroundUIView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:dimBackgroundUIView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:dimBackgroundUIView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
}
-(void) addConstraintsForMainView {
[mainView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:mainView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:mainView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:mainView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:mainView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
}
#end
note: you can optimise the code by using graphical interface on Xcode but i had to write code programmatically to be able to test it
so the idea is to:
create UIView name it what you want (dimBackgroundUIView) than set
the backgroundcolor as Black and set alfa to 0.1 or 0.2 or....
add this 2 line of code when you need to dim the background:[self.view addSubview:dimBackgroundUIView];
[self addConstraintsForDimView];
and add this 2 line of code when you want to remove the dimming
background: if(dimBackgroundUIView) [dimBackgroundUIView removeFromSuperview];