Xcode custom segue - Opposite of Cover Vertical (top to bottom) - COMPLETE NEWBIE - xcode

I have been trying for some time to create a custom segue that does the opposite of the cover vertical (to create a from top to bottom effect animation).I had looked through other questions, google and youtube but cannot get it working.I am completely new to the classes etc. so any help in a step by step guide or video would be so good. I believe that this URL is trying to do something similar to what I want:
http://jrwren.wrenfam.com/blog/2012/02/01/storyboard-custom-segue-for-custom-pushviewcontroller-animation/ , I just cannot get it working.
If any of you know a way to create a segue that is a top to bottom version of the cover vertical segue could you please help me out?

Well, you would want to create a subclass of UIStoryBoardSegue, like the walkthrough shows you, however under the Storyboard class' .m file(implementation) you would want the following code as your -(void)perform: method -
-(void)perform{
UIViewController *sourceViewController = (UIViewController *) self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) self.destinationViewController;
[sourceViewController.view addSubview:destinationViewController.view];
[destinationViewController.view setFrame:sourceViewController.view.window.frame];
[destinationViewController.view setTransform:CGAffineTransformMakeTranslation(0, -sourceViewController.view.frame.size.height)];
[destinationViewController.view setAlpha:1.0];
[UIView animateWithDuration:0.75
delay:0.0
options:UIViewAnimationOptionTransitionFlipFromTop
animations:^{
[destinationViewController.view setTransform:CGAffineTransformMakeTranslation(0, 0)];
[destinationViewController.view setAlpha:1.0];
}
completion:^(BOOL finished){
[destinationViewController.view removeFromSuperview];
[sourceViewController presentViewController:destinationViewController animated:NO completion:nil];
}];}
Hopefully this is helpful.

Building on #dmason82's answer, translating to Swift, and simplifying a little, this works for me:
class UIStoryboardSegueFromTop: UIStoryboardSegue {
override func perform() {
let src = self.sourceViewController as UIViewController
let dst = self.destinationViewController as UIViewController
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransformMakeTranslation(0, -src.view.frame.size.height)
UIView.animateWithDuration(1.0, animations: {
dst.view.transform = CGAffineTransformMakeTranslation(0, 0)
}) { (Finished) in
src.presentViewController(dst, animated: false, completion: nil)
}
}
}

Here is my implementation of an Opposite Cover Vertical Segue using Storyboards
https://github.com/viccarre/OppositeCoverVerticalSegue

Hi I just tried the two other answer but it doesn't work as you ask.
In order to create a custom segue looking the exact reverse of cover vertical (modal segue default) I created this code :
- (void)perform
{
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
[sourceViewController presentViewController:destinationViewController animated:NO completion:nil];
[destinationViewController.view addSubview:sourceViewController.view];
[sourceViewController.view setTransform:CGAffineTransformMakeTranslation(0, 0)];
[sourceViewController.view setAlpha:1.0];
[UIView animateWithDuration:0.75
delay:0.0
options:UIViewAnimationOptionTransitionFlipFromBottom
animations:^{
[sourceViewController.view setTransform:CGAffineTransformMakeTranslation(0,destinationViewController.view.frame.size.height)];
[sourceViewController.view setAlpha:1.0];
}
completion:^(BOOL finished){
[sourceViewController.view removeFromSuperview];
}];
}
#end

To complete dmason's answer and have your controller unwind in the opposite direction - going back up from where it came, do this in the presented view controller:
[UIView animateWithDuration:0.75
delay:0.0
options:UIViewAnimationOptionTransitionFlipFromBottom
animations:^{
[self.view setTransform:CGAffineTransformMakeTranslation(0, -self.view.frame.size.height)];
}
completion:^(BOOL finished){
[self dismissViewControllerAnimated:NO completion:nil];
}
];
Bergy's answer didn't work for me.

dmason's answer is great, and if you want to add an unwind segue so your modal animates properly when it's closed, your perform action could look like this (in a new custom segue class)
- (void)perform
{
UIViewController *sourceViewController = self.sourceViewController;
UIViewController *destinationViewController = self.destinationViewController;
[sourceViewController dismissViewControllerAnimated:NO completion:nil];
[destinationViewController.view addSubview:sourceViewController.view];
[UIView animateWithDuration:0.75
animations:^{
sourceViewController.view.center = CGPointMake(sourceViewController.view.center.x, sourceViewController.view.center.y-600);
}
completion:^(BOOL finished){
[sourceViewController.view removeFromSuperview];
}
];
}

I advise you to use the Hero library to achieve this. It adds a lot of animations and transitions, notably between view controllers : https://github.com/lkzhao/Hero/blob/master/README.md
There is an transition called "Cover", and you can choose the direction (left, top, right, bottom)

Related

dismissViewControllerAnimated differs in ios8 and ios7

I have a launchViewController A and two other view controllers B and C. In the app, present sequence is A->B->C. I also have a need to dismiss C and directly back to A.
The problem is:
In iOS7, I call [self dismissViewControllerAnimated:false completion:^{}] in A. the functions viewWillAppear and viewDidAppear in B view controller will not be called.
But in iOS8, things are different. viewWillAppear and viewDidAppear will be called in B view controller. This results in a flashes of B's contents when dismiss.
Can anyone help me to find a way to fix this.
if you want to dismiss current ViewController in Which you are you should use
[self.presentingViewController dismissViewControllerAnimated:false completion:^{}]
instead of [self dismissViewControllerAnimated:false completion:^{}]
I fixed the issue by using this:
- (void)dismissDeepViewControllerAnimated: (BOOL)flag completion: (void (^)(void))completion {
if([UIDevice currentDevice].systemVersion.floatValue >= IOSCapVersion){
UIView *window = ((AppDelegate *)[UIApplication sharedApplication].delegate).window;
UIView *view = [window snapshotViewAfterScreenUpdates:false];
[((AppDelegate *)[UIApplication sharedApplication].delegate).window addSubview:view];
[self dismissViewControllerAnimated:false completion:^{
[UIView animateWithDuration:AnimationDurationDismissViewController animations:^{
view.alpha = 0;
}completion:^(BOOL finished) {
if (finished) {
[view removeFromSuperview];
}
}];
completion();
}];
}
else{
[self dismissViewControllerAnimated:flag completion:^{
completion();
}];
}
}

stop animation using UIView in UILabel XCode

I have a doubt about to stop animation in UILabel,
I'm using a UIView to animate a UILabel, my code is similar to:
-(void)pickerRowTransformItem:(int)item{
//Get UILabel
UILabel *labelTem = (UILabel *)[self.picker viewWithTag:item];
if (labelTem){
[UIView animateWithDuration: 0.10
delay:0.0
options:UIViewAnimationTransitionNone | UIViewAnimationOptionCurveLinear
animations:^{
labelTem.transform = tr //Scale transform;
labelTem.textColor = color //Change color;
}
completion:^(BOOL finished){
}];
}
}
to stop animation, should I use?
-(void)pickerRowTransformItemStopAnimation:(int)item{
//Get UILabel
UILabel *labelTem = (UILabel *)[self.picker viewWithTag:item];
if (labelTem){
[labelTem.layer removeAllAnimations]; //stop animation??
}
}
or, which is the best way to stop a animation?
thanks!
I think the following article will benefit you. I believe this person solved the same question that you have. Try adding completion:NULL to your code instead of labelTem.layer removeAllAnimations.
How can I stop an UIViewAnimationOptionRepeat UIView Animation?

Custom Segue not allowing IBActions in destination controller

I have two View Controllers with views. The first one is a login screen and the second one fetches stuff from the web (irrelevant).
I used a custom segue animation and had to do some weird stuff with the superview to get the sourceViewController.view to be "on top" (visually) of the destinationViewController.view
I can only assume this is why when i try to call IBAction methods from the second view they won't call.
Here is the segue class implementation:
- (void) perform {
UIViewController *sourceViewController = (UIViewController *) self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) self.destinationViewController;
UIView *parent = sourceViewController.view.superview;
[sourceViewController.view removeFromSuperview];
[parent addSubview: destinationViewController.view];
[parent addSubview:sourceViewController.view];
sourceViewController.view.layer.masksToBounds = NO;
sourceViewController.view.layer.cornerRadius = 8; // if you like rounded corners
sourceViewController.view.layer.shadowOffset = CGSizeMake(0,0);
sourceViewController.view.layer.shadowRadius = 10;
sourceViewController.view.layer.shadowOpacity = 1;
destinationViewController.view.frame = CGRectMake(0, 20, destinationViewController.view.frame.size.width, destinationViewController.view.frame.size.height);
sourceViewController.view.frame = CGRectMake(0, 20, sourceViewController.view.frame.size.width, sourceViewController.view.frame.size.height);
[UIView animateWithDuration:.6
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^{
sourceViewController.view.frame = CGRectMake(-sourceViewController.view.frame.size.width-10, 20, sourceViewController.view.frame.size.width, sourceViewController.view.frame.size.height);
}
completion:^(BOOL finished){
//[destinationViewController.view removeFromSuperview];
[sourceViewController.navigationController pushViewController:destinationViewController animated:NO];
}];
}
My question is, can removing the source view from its superview and playing around with that ruin the way that IBActions are called on the second view?
The IBAction methods just crash the app, on a button push for example.
I fixed my issue by changing my code to this:
UIViewController *sourceViewController = (UIViewController *) self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) self.destinationViewController;
UIView *parent = sourceViewController.view.superview;
[parent addSubview:destinationViewController.view];
[parent sendSubviewToBack: destinationViewController.view];
sourceViewController.view.layer.masksToBounds = NO;
sourceViewController.view.layer.cornerRadius = 8; // if you like rounded corners
sourceViewController.view.layer.shadowOffset = CGSizeMake(0,0);
sourceViewController.view.layer.shadowRadius = 10;
sourceViewController.view.layer.shadowOpacity = 1;
destinationViewController.view.frame = CGRectMake(0, 20, destinationViewController.view.frame.size.width, destinationViewController.view.frame.size.height);
sourceViewController.view.frame = CGRectMake(0, 20, sourceViewController.view.frame.size.width, sourceViewController.view.frame.size.height);
[UIView animateWithDuration:.6
delay:0.0
options:UIViewAnimationCurveEaseInOut
animations:^{
sourceViewController.view.frame = CGRectMake(-sourceViewController.view.frame.size.width-10, 20, sourceViewController.view.frame.size.width, sourceViewController.view.frame.size.height);
}
completion:^(BOOL finished){
[sourceViewController presentViewController:destinationViewController animated:NO completion:NULL];
}];
A few things changed, but notably, i used
[sourceViewController presentViewController:destinationViewController animated:NO completion:NULL];
to initialise the controller properly.
Hope this helps someone else out in the future.

How would I do this iOS animation on OSX?

I have a very simple animation in iOS that fades a view, resizes a container to fit another view, then fades that other view back in. It's quite easy to do and very straightforward.
I've been trying to do something pretty much exactly like this on OSX, but I haven't been able to figure out how to do it. The animation stuff on OSX feels so clunky and difficult compared to iOS.
Any help would be much appreciated!!
Thanks! :)
// Fade out viewOne, resize frame to fit viewTwo, fade in viewTwo
[UIView animateWithDuration: 0.15
animations: ^{
[viewOne setAlpha:0.0];
}
completion: ^(BOOL finished) {
[UIView animateWithDuration: 0.2
animations: ^{
[self setFrame: [viewTwo frame]];
}
completion: ^(BOOL finished) {
[viewTwo setAlpha: 0.0];
[self addSubview: viewTwo];
[UIView animateWithDuration: 0.15
animations: ^{
[viewTwo setAlpha:1.0];
}];
}];
}];
I've written a small class that uses blocks to accomplish essentially the same thing as above when using the animator proxy on OSX.
Please note, this class is not thread safe and hasn't undergone any specific or stressful tests.
//Interface
#interface MZAnimator : NSObject{}
+ (void)animateWithDuration:(NSTimeInterval)duration
animation:(void (^)(void))animationBlock;
+ (void)animateWithDuration:(NSTimeInterval)duration
animation:(void (^)(void))animationBlock
completion:(void (^)(void))completionBlock;
#end
//Implementation
#interface MZAnimator ()
+ (void)runEndBlock:(void (^)(void))completionBlock;
#end
#implementation MZAnimator
+ (void)animateWithDuration:(NSTimeInterval)duration
animation:(void (^)(void))animationBlock
{
[self animateWithDuration:duration animation:animationBlock completion:nil];
}
+ (void)animateWithDuration:(NSTimeInterval)duration
animation:(void (^)(void))animationBlock
completion:(void (^)(void))completionBlock
{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:duration];
animationBlock();
[NSAnimationContext endGrouping];
if(completionBlock)
{
id completionBlockCopy = [[completionBlock copy] autorelease];
[self performSelector:#selector(runEndBlock:) withObject:completionBlockCopy afterDelay:duration];
}
}
+ (void)runEndBlock:(void (^)(void))completionBlock
{
completionBlock();
}
#end
You can use:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration:2.0];
//Animation code
} completionHandler:^{
//Completion Code
NSLog(#"Completed");
}];

UIBarButtonItem frame? [duplicate]

UIBarButtonItem does not extend UIView, so there is nothing like a frame property.
But is there any way I can get what is it's CGRect frame, relative to the application UIWindow?
Do you like to use private APIs? If yes,
UIView* view = thatItem.view;
return [view convertRect:view.bounds toView:nil];
Of course no one wants this when targeting the AppStore. A more unreliable method, and also uses undocumented features, but will pass Apple's test, is to loop through the subviews to look for the corresponding button item.
NSMutableArray* buttons = [[NSMutableArray alloc] init];
for (UIControl* btn in theToolbarOrNavbar.subviews)
if ([btn isKindOfClass:[UIControl class]])
[buttons addObject:btn];
UIView* view = [buttons objectAtIndex:index];
[buttons release];
return [view convertRect:view.bounds toView:nil];
The index is the index to your bar item in the array of .items, after removing all blank items. This assumes the buttons are arranged in increasing order, which may not be. A more reliable method is to sort the buttons array in increasing .origin.x value. Of course this still assumes the bar button item must inherit the UIControl class, and are direct subviews of the toolbar/nav-bar, which again may not be.
As you can see, there are a lot of uncertainty when dealing with undocumented features. However, you just want to pop up something under the finger right? The UIBarButtonItem's .action can be a selector of the form:
-(void)buttonClicked:(UIBarButtonItem*)sender event:(UIEvent*)event;
note the event argument — you can obtain the position of touch with
[[event.allTouches anyObject] locationInView:theWindow]
or the button view with
[[event.allTouches anyObject] view]
Therefore, there's no need to iterate the subviews or use undocumented features for what you want to do.
I didn't see this option posted (which in my opinion is much simpler), so here it is:
UIView *barButtonView = [barButtonItem valueForKey:#"view"];
In iOS 3.2, there's a much easier way to show an Action Sheet popover from a toolbar button. Merely do something like this:
- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
UIActionSheet *popupSheet;
// Prepare your action sheet
[popupSheet showFromBarButtonItem:sender animated:YES];
}
This is the implementation I use for my WEPopover project: (https://github.com/werner77/WEPopover):
#implementation UIBarButtonItem(WEPopover)
- (CGRect)frameInView:(UIView *)v {
UIView *theView = self.customView;
if (!theView.superview && [self respondsToSelector:#selector(view)]) {
theView = [self performSelector:#selector(view)];
}
UIView *parentView = theView.superview;
NSArray *subviews = parentView.subviews;
NSUInteger indexOfView = [subviews indexOfObject:theView];
NSUInteger subviewCount = subviews.count;
if (subviewCount > 0 && indexOfView != NSNotFound) {
UIView *button = [parentView.subviews objectAtIndex:indexOfView];
return [button convertRect:button.bounds toView:v];
} else {
return CGRectZero;
}
}
#end
As long as UIBarButtonItem (and UITabBarItem) does not inherit from UIView—for historical reasons UIBarItem inherits from NSObject—this craziness continues (as of this writing, iOS 8.2 and counting ... )
The best answer in this thread is obviously #KennyTM's. Don't be silly and use the private API to find the view.
Here's a oneline Swift solution to get an origin.x sorted array (like Kenny's answer suggests):
let buttonFrames = myToolbar.subviews.filter({
$0 is UIControl
}).sorted({
$0.frame.origin.x < $1.frame.origin.x
}).map({
$0.convertRect($0.bounds, toView:nil)
})
The array is now origin.x sorted with the UIBarButtonItem frames.
(If you feel the need to read more about other people's struggles with UIBarButtonItem, I recommend Ash Furrow's blog post from 2012: Exploring UIBarButtonItem)
I was able to get Werner Altewischer's WEpopover to work by passing up the toolbar along with the
UIBarButton:
Mod is in WEPopoverController.m
- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item toolBar:(UIToolbar *)toolBar
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated
{
self.currentUIControl = nil;
self.currentView = nil;
self.currentBarButtonItem = item;
self.currentArrowDirections = arrowDirections;
self.currentToolBar = toolBar;
UIView *v = [self keyView];
UIButton *button = nil;
for (UIView *subview in toolBar.subviews)
{
if ([[subview class].description isEqualToString:#"UIToolbarButton"])
{
for (id target in [(UIButton *)subview allTargets])
{
if (target == item)
{
button = (UIButton *)subview;
break;
}
}
if (button != nil) break;
}
}
CGRect rect = [button.superview convertRect:button.frame toView:v];
[self presentPopoverFromRect:rect inView:v permittedArrowDirections:arrowDirections animated:animated];
}
-(CGRect) getBarItemRc :(UIBarButtonItem *)item{
UIView *view = [item valueForKey:#"view"];
return [view frame];
}
You can get it from the UINavigationBar view. The navigationBar is a UIView which has 2 or 3 custom subviews for the parts on the bar.
If you know that the UIBarButtonItem is currently shown in the navbar on the right, you can get its frame from navbar's subviews array.
First you need the navigationBar which you can get from the navigationController which you can get from the UIViewController. Then find the right most subview:
UINavigationBar* navbar = curViewController.navigationController.navigationBar;
UIView* rightView = nil;
for (UIView* v in navbar.subviews) {
if (rightView==nil) {
rightView = v;
} else if (v.frame.origin.x > rightView.frame.origin.x) {
rightView = v; // this view is further right
}
}
// at this point rightView contains the right most subview of the navbar
I haven't compiled this code so YMMV.
This is not the best solution and from some point of view it's not right solution and we can't do like follow because we access to object inside UIBarBattonItem implicitly, but you can try to do something like:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[button setImage:[UIImage imageNamed:#"Menu_Icon"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(didPressitem) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.rightBarButtonItem = item;
CGPoint point = [self.view convertPoint:button.center fromView:(UIView *)self.navigationItem.rightBarButtonItem];
//this is like view because we use UIButton like "base" obj for
//UIBarButtonItem, but u should note that UIBarButtonItem base class
//is NSObject class not UIView class, for hiding warning we implicity
//cast UIBarButtonItem created with UIButton to UIView
NSLog(#"point %#", NSStringFromCGPoint(point));
as result i got next:
point {289, 22}
Before implement this code, be sure to call [window makeKeyAndVisible] in your Applition delegate application:didFinishLaunchingWithOptions: method!
- (void) someMethod
{
CGRect rect = [barButtonItem convertRect:barButtonItem.customview.bounds toView:[self keyView]];
}
- (UIView *)keyView {
UIWindow *w = [[UIApplication sharedApplication] keyWindow];
if (w.subviews.count > 0) {
return [w.subviews objectAtIndex:0];
} else {
return w;
}
}
I handled it as follows:
- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
UIView* view = [sender valueForKey:#"view"]; //use KVO to return the view
CGRect rect = [view convertRect:view.bounds toView:self.view];
//do stuff with the rect
}

Resources