dismissViewControllerAnimated differs in ios8 and ios7 - ios8

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();
}];
}
}

Related

presentViewController in AppDelegate with delay in iOS8

So I had a full working solution in iOS7 that displays a LoginViewController via presentViewController in the AppDelegate's didFinishLaunching.
Basically I am doing something like this:
UIViewController *backgroundViewController = ...
self.window.rootViewController = backgroundViewController;
[self.window makeKeyAndVisible];
[self.window.rootViewController presentViewController:loginViewController
animated:NO ...]
In iOS8 I see a jump. First I see the backgroundViewController then after about 1 second or so the login appears.
So, how can I prevent this jump in iOS8?
I am seeing that are a ton of developers with this kind of problem but still didn't find a solution.
Also a hack (for now), but just one line of code
Add the view of the view controller you're presenting to the window before presentation
UIViewController *viewController = [[UIViewController alloc] init];
[viewController.view setBackgroundColor:[UIColor greenColor]];
// Temporary iOS8 fix for 'presentation lag' on launch
[self.window addSubview:viewController.view];
[self.window.rootViewController presentViewController:viewController animated:NO completion:nil];
If you are presenting a navigation controller than add the navigation controller's view instead of its top view controller.
I have a quick hacky fix:
//Make a screenshot of the ViewController first, or use a real image if you want
__block UIImageView *fakeImageView = [[UIImageView alloc] initWithImage:image];
fakeImageView.frame = vc.view.frame;
[self.view addSubview:fakeImageView];
[self presentViewController:vc animated:animated completion:^{
[fakeImageView removeFromSuperview];
fakeImageView = nil;
}];
It is not good for long term, but can quickly fix this issue without changing too much code.
Waiting for better solutions.
You can set the window to an instance of a temporary controller.
self.window.backgroundColor = [UIColor whiteColor]; //do some styling etc.
self.window.rootViewController = [LoginViewController new];
[self.window makeKeyAndVisible];
From the set controller (LoginViewController) you can push your real login controller with the desired transition. Once the login sequence is over you can make a transition from the login controller to the default application root view controller.
[UIView transitionWithView:[AppGlobal sharedApp].applicationWindow
duration:0.75
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[AppGlobal sharedApp].applicationWindow.rootViewController = [AppRootViewController new];
} completion:nil];
I have also faced the same problem in iOS8 and I found this solution:
ABCViewController *obj = [[ABCViewController alloc] initWithNibName:#"ABCViewController" bundle:nil];
CATransition *transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromBottom;
transition.delegate = self;
[self.navigationControler.view.layer addAnimation:transition forKey:nil];
[appDelegate.navigationControler obj animated:NO];
obj = nil;
I hope this solution can help you!
This should work:
call [loginViewController view]
Before presenting it.

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

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)

UINavigationController - rotation issues iOS6

I've subclassed the UINavigationController as follows:
-(NSUInteger)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
-(BOOL)shouldAutorotate {
return self.topViewController.shouldAutorotate;
}
However I've come unstuck on pushing a view controller in an initial orientation differing to its' parent. In my case the parent view is portrait only:
-(BOOL)shouldAutorotate {
return NO;
}
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
But I wish the pushed view to be landscape only - it'll only behave as such after manually changing the orientation of the device.
-(BOOL)shouldAutorotate {
return YES;
}
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
I'd much appreciate anyone pointing me in the right direction. (I've only got a hacky solution so far...)
EDIT:
Following shows how I'm initiating the subclassed UINavigationController
PortraitView *vc = [[PortraitView alloc] init];
SubClassedNav *navController = [[SubClassedNav alloc] initWithRootViewController:vc];
[navController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self presentViewController:navController animated:YES completion:nil];
And how I subsequently push a new view:
LandscapeView *vc = [[LandscapeView alloc] init];
[[self navigationController] pushViewController:vc animated:YES];
Double check that you setup [window setRootViewController:controller] in your AppDelegate.

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
}

xcode Removing Some Subviews from view

Greetings all,
I am a noob and I have been trying to work through this for a few days.
I am adding images to a view via UItouch. The view contains a background on top of which the new images are add. How do I clear the images I am adding from the subview, without getting rid of the UIImage that is the background. Any assistance is greatly appreciated. Thanks in Advance.
here is the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event {
NSUInteger numTaps = [[touches anyObject] tapCount];
if (numTaps==2) {
imageCounter.text =#"two taps registered";
//__ remove images
UIView* subview;
while ((subview = [[self.view subviews] lastObject]) != nil)
[subview removeFromSuperview];
return;
}else {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
CGRect myImageRect = CGRectMake((touchPoint.x -40), (touchPoint.y -45), 80.0f, 90.0f);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"pg6_dog_button.png"]];
myImage.opaque = YES; // explicitly opaque for performance
[self.view addSubview:myImage];
[myImage release];
[imagesArray addObject:myImage];
NSNumber *arrayCount =[self.view.subviews count];
viewArrayCount.text =[NSString stringWithFormat:#"%d",arrayCount];
imageCount=imageCount++;
imageCounter.text =[NSString stringWithFormat:#"%d",imageCount];
}
}
What you need is a way of distinguishing the added UIImageView objects from the background UIImageView. There are two ways I can think of to do this.
Approach 1: Assign added UIImageView objects a special tag value
Each UIView object has a tag property which is simply an integer value that can be used to identify that view. You could set the tag value of each added view to 7 like this:
myImage.tag = 7;
Then, to remove the added views, you could step through all of the subviews and only remove the ones with a tag value of 7:
for (UIView *subview in [self.view subviews]) {
if (subview.tag == 7) {
[subview removeFromSuperview];
}
}
Approach 2: Remember the background view
Another approach is to keep a reference to the background view so you can distinguish it from the added views. Make an IBOutlet for the background UIImageView and assign it the usual way in Interface Builder. Then, before removing a subview, just make sure it's not the background view.
for (UIView *subview in [self.view subviews]) {
if (subview != self.backgroundImageView) {
[subview removeFromSuperview];
}
}
A more swiftly code for approach #1 in only one functional line of code :
self.view.subviews.filter({$0.tag == 7}).forEach({$0.removeFromSuperview()})

Resources