I'm new to Objective-C programming and I'm having a little trouble understanding how I transition between two views.
Basically, I have my main view (the view that loads up when the application opens) and I want to transition to a new view on pressing a button. The user will not need to go back to the main view after pressing the button -- it's basically a title screen.
Could someone please briefly explain the steps I would need to take to make this happen?
Thanks a lot.
You could make use of UIView's class method transitionFromView:toView:duration:options:completion:.
A call to switch from viewA to viewB could look like this:
[UIView transitionFromView:viewA
toView:viewB
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:^(BOOL finished){
[viewA release];
}];
As you mentioned the user won't get back to the mainView I added something to the completion parameter to get rid of viewA afterwards.
You can find the animation options in the constants of the UIView class documentation.
If it's going to be a modal view, you can push on a new modal view controller which will then later be popped off when work is complete. These are usually intended for small amounts of work
[container presentModalViewController:yourNavigationViewController animated:YES];
Otherwise you can modify the UIView stack using the two UIView transition class methods:
+ transitionWithView:duration:options:animations:completion:
+ transitionFromView:toView:duration:options:completion:
For more info on these check out Apple's UIView class docs.
If you don't just want to copy & paste the code you should follow Apple's sample code step by step and remember you can download it:
ViewTransitions sample application
The ViewTransitions sample application demonstrates how to perform transitions between two views using built-in Core Animation transitions. By looking at the code, you'll see how to use a CATransition object to set up and control transitions.
Related
I am developing a small test project in Xcode 10 for iOS. I am basically testing custom UIStoryboardSegues. I have prepared a test project to demonstrate the misbehaviour.
During the segue animations, a black shadow shows on screen. You can see it in the test project because I have set the animation time to 5 seconds.
Any help is greatly appreciated. Thanks
What you're seeing is the view's changing opacity near the beginning and end of the animation, so the "shadow" you're seeing is actually the backing window. While it might not be perfect, a quick fix is to change the background color of your window to match the background color of your destination view controller (then set it back after the transition completes if needed).
i.e.:
// hold onto the previous window background color
UIColor *previousWindowBackgroundColor = sourceViewController.view.window.backgroundColor;
// switch the window background color to match the destinationController's background color temporarily
sourceViewController.view.window.backgroundColor = destinationController.view.backgroundColor;
// do the transition
[sourceViewController.navigationController pushViewController:destinationController animated:NO];
// switch the window color back after the transition duration from above
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(animationDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// make sure we still have a handle on the destination controller
if (destinationController) {
destinationController.view.window.backgroundColor = previousWindowBackgroundColor;
}
});
You'll also need to switch the animation to the sourceView's layer:
[sourceViewController.view.layer addAnimation:transition forKey:nil];
But this is really just a workaround to make it look slightly better. Instead of using custom Storyboard Segues I would highly recommend using Custom Animators which will give you much more control over the animation transition.
My answer here: IOS/Objective-C: Possible to Use Custom Segue in Modal Transition in Absence of a Storyboard Segue? has a full example of custom SlideUp/SlideDown animators.
And here's links to the documentation for it:
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html
https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate?language=objc
https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning?language=objc
Required:
I want to enable iOS7 swipe to back feature with custom navigation back button item.
Current Implementation:
After researching a lot, I found the following solution to be best:
Set the delegate of the gesture recognizer as follows
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
This, creates a lot of bugs as mentioned in this stackoverflow answer. To avoid that, subclassing the UINavigationController seems to be the only feasible option. I did that as mentioned in this blog by Keighl.
Problem:
Basic swipe to back feature is working, but the strange thing is that, sometimes, the same viewController that is being dismissed, appears again after the pop action is completed.
i.e. suppose the navigation stack looks like A -> B. Popping B will again bring up B. This keeps on happening until eventually the viewController B actually gets dismissed and A appears.
This happens to all views in all viewController objects and not just to a specific one.
Also, I have ensured that the push method is called only once at all places.
I also tried logging the navigation stack at each point, but there is only one instance of each viewController.
Point to note:
I need to disable the swipe feature in certain views. I did this by writing the code to disable and enable the swipe gesture in viewDidAppear and viewDidDisappear respectively.
Please provide your valuable suggestions or a solution to this problem. Thanks!
Short answer: You should add a UIScreenEdgePanGestureRecognizer to your view controller if you want to add a pop gesture where none exists. Modifying the existing interactivePopGestureRecognizer is probably not the right approach. Do this:
[self addGestureRecognizer:({
UIScreenEdgePanGestureRecognizer *gesture =
[[UIScreenEdgePanGestureRecognizer alloc]
initWithTarget:self action:#selector(pop)];
gesture;
})];
and
-(void)pop {
// pop your view controller here
}
Long answer: Forcing the interactivePopGestureRecognizer.delegate is what breaks your code.
If you need to cast self as such:
self.navigationController.interactivePopGestureRecognizer.delegate =
(id<UIGestureRecognizerDelegate>)self;
...it is because self is not a UIGestureRecognizerDelegate. The following should compile, link, build and run or you are setting yourself up for trouble:
self.navigationController.interactivePopGestureRecognizer.delegate = self;
Note that being a UIGestureRecognizerDelegate specifically allows you to tweak a gesture's behavior at runtime, assuming you are implementing one of the following and ensuring that the tweak applies to a gesture you own:
gestureRecognizerShouldBegin:
gestureRecognizer:shouldReceiveTouch:
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
By constantly changing the delegate of that interactivePopGestureRecognizer you did not create, all you are doing is preventing the iOS behavior to take place.
From the documentation
UINavigationController -interactivePopGestureRecognizer
The gesture recognizer responsible for popping the top view controller off the navigation stack. (read-only)
In plain English: Use this value is you need to combine that gesture with your own gesture. But you are not supposed to modify its behavior:
...You can use this property to retrieve the gesture recognizer and tie it to the behavior of other gesture recognizers in your user interface...
I want to show a logo UIView always on top when the app running,
I know there is a way to do that,add same UIView to every UIViewController,
but I think this is not the best way to do that.
when i have lot of pages,and modify the logo UIView,must modify it every page.
Did someone have better way to do this?
thanks.
look like this:
Since you only every have one window per app, and view's don't have levels, you have to make sure that view stays on top of the hierarchy, no matter what. One relatively easy way is to add it directly to the window above the rest of the interface (the navigation controller):
In applicationDidLaunch:
// After the main navigation controller or tab controller has been added
// either programmatically or in the xib:
UIImage *logo = [UIImage imageNamed:#"logo.png"];
UIImageView *logoView = [[UIImageView alloc] initWithImage:logo];
[self.window addSubview:logoView];
Actually, I think that (a) creating a subclass of UIView that shows your logo and has all the necessary setup in it and then (b) adding this subclass to each view controller is the cleanest and most manageable way to do this.
The reason I prefer this method over adding the view to the window is because if you ever have a view that you don't want to show the logo, you won't need to show and hide something you added to the window. Also, adding directly to the window may cause rotation challenges on certain iOS devices in my experience, depending on what you're doing.
Also, to make sure your logo view is always on top of the view hierarchy, you can do two things:
If the view already exists, you can bring it to front using [UIView bringSubviewToFront:]
[myParentView bringSubviewToFront:myLogoSubview];
If you are creating the view, it will be on top when you add it with [UIView addSubview:]
// Set up myLogoSubview first here with alloc+init, etc.
[myParentView addSubview:myLogoSubview];`
It looks like in your image you would replace myParentView with self.view and myLogoSubview with the view you're looking to keep on top, but this is just my assumption based on your image.
Is there a way to use custom segues to individually animate several different subviews.
For example, I want my modal view to appear by the UINavigationBar fading in (as the source destination's UINavigationBar fades out) and then a UITableView to slide down the screen 'over' the source destination's view controller.
When I try to implement this in the - (void)perform method. My properties don't animate using [UIView animateWithDuration: animations: completion:].
Can anyone provide me with a solution?
Thanks in advance!
You can certainly use custom segues to achieve this - however, I don't think you'll get much help without more details about the setup of your view controllers.
Everything you describe is correct: to create a custom segue you animate the views inside your sourceViewController and destinationViewController inside the segue's perform: method. If they're not animating you might want to check that your segue is actually getting called (you can use breakpoints in the debugger to check this), or that the views you're trying to access inside your view controllers actually exist at that point in time (again, something you can check using the debugger).
For a solution specific to your app you're almost certainly going to have to provide more details about the two view controllers you're trying to transition between. Perhaps you could post your perform: method.
Hi there and thank you in advice for your help. I have a really strange problem while working with ViewControllers in Xcode4. First of all I have to say that I'm not using storyboards and I prefer to create any UI element programmatically. So I've set a UIButton and I want that, when pressed, it brings me to a new view controller. This is the code I'm using for a button:
-(void)settingsAndExportHandle:(UIButton *)buttonSender {
SettingsViewController* settingView = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
settingView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:settingView animated:YES];
}
This buttons is initialized and allocated in the viewDidLoad method of the RootViewController. I want to switch to the other view controller (in this case SettingsViewController) when I press the button.
The strange thing is that when I press the button, the animation that flips the controllers goes well, but when it finishes I obtain the EXACT same things that I had on the RootViewControllers (same custom views, same buttons, same all!). The question is: what I'm missing?? I have to say that I use ARC (automatic reference counting) so I can't release or dealloc the views and buttons I've created on my RootViewController.
Any help will be appreciated. Thank you all!
Pushing and and modally presenting view controllers does not deallocate the view controller that presented them. It simply adds the additional view controller to the stack. You'll need to implement a callback method so that when the user hits the button to flip back to root view controller, your settings view controller lets the root view controller know what's about to happen so you can call a method you've written to reset the interface back to whatever state you need it at. You may also be able to use viewWillAppear: but that's a little messy.
However, according to the Apple Human Interface Guidelines, the user expects that when they push a view controller or modally present it, the view controller they were on will save state and be exactly the way they left it when they came back. It's disconcerting and annoying when state is not preserved while in a navigation controller context. It is especially annoying when it's modally presented.
Think about this - A user is in a hypothetical Mail app. They start typing out an email and set a font size and a color. They tap on the add attachment button, which brings up a modal view controller that allows them to select a picture. They select the picture and the modal view is dismissed, and in your implementation, the mail composing interface would have reset and the email content would be gone or at the very least the selected font size and color would be back to the default. That's not a good experience.