Custom transition between UIViewControllers does not work - ios

I have object that manage custom transition:
#interface NavigationManager : NSObject <UIViewControllerAnimatedTransitioning>
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext;
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext;
#property (nonatomic, assign) NSTimeInterval presentationDuration;
#property (nonatomic, assign) NSTimeInterval dismissalDuration;
#property (nonatomic, assign) BOOL isPresenting;
#end
#interface NavigationManager()
#property (nonatomic, strong) id<UIViewControllerContextTransitioning> transitionContext;
#end
#implementation NavigationManager
#synthesize presentationDuration, dismissalDuration, isPresenting;
-(id)init{
self = [super init];
if(self){
self.presentationDuration = 1.0;
self.dismissalDuration = 0.5;
}
return self;
}
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return self.isPresenting ? self.presentationDuration : self.dismissalDuration;
}
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
self.transitionContext = transitionContext;
if(self.isPresenting){
[self executePresentation:transitionContext];
}
else{
[self executeDismiss:transitionContext];
}
}
...
#end
And in class that should process implement transitions:
#interface ViewController : UIViewController <UIViewControllerTransitioningDelegate>
#property (nonatomic, strong) NavigationManager navigationManager;
..
#end
#implementation
...
//Here I`m doing navigation to new UIViewController
- (void)navigateToNewScreen
{
DetailedNewsController *secVC = [[DetailedNewsController alloc] init];
secVC.fullDescription = fullDescription;
secVC.headerImage = a.imageView.image;
self.transitioningDelegate = self;
[self.navigationController pushViewController:secVC animated:YES];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source
{
self.animationController.isPresenting = YES;
return self.animationController;
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
self.animationController.isPresenting = NO;
return self.animationManager;
}
#end
Default animation of transitioning is performed. Also navigation controller bar is not displayed after navigation.
UPDATE:
I thing problem in way how I`m creating UINavigationController in AppDelegate:
firstVC = [[ViewController alloc] init];
navController = [[UINavigationController alloc] initWithRootViewController:firstVC];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor blackColor];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];

I'll make an answer with the new information you have provided. The reason I make it an answer is that I just don't want to give you some copy paste code, but to try to give a brief explanation behind it. If I understand correctly, you now want to present a ViewController with a custom transition.
So you got the custom transition working by changing your code to this:
secVC.transitioningDelegate = self;
secVC.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController:secVC animated:YES completion:nil];
Since we got that up and running, what's currently missing is the NavigationBar of the ViewController you want to show. Since you're presenting a ViewController, it won't be contained within the existing NavigationController stack in which the the ViewController you're presenting is within.
Therefore, you need to wrap your VC you want to present within a UINavigationController.
[self presentViewController:[[UINavigationController alloc] initWithRootViewController:secVC] animated:YES completion:nil];

Related

Assigning to properties inside custom init method

I'm trying to write custom init method for my UIViewController. Here is the method itself:
- (instancetype)initWithBottomViewController:(UIViewController *)bottomViewController andTopViewController:(UIViewController *)topViewController {
self = [super init];
if (self) {
self.bottomViewController = bottomViewController;
self.topViewController = topViewController;
}
return self;
}
- (void)setupBottomViewController {
/*crashes here because self.bottomViewController appears to be nil*/
[self addChildViewController:self.bottomViewController];
[self.bottomViewController.view setFrame:self.view.bounds];
[self.view addSubview:self.bottomViewController.view];
[self.bottomViewController didMoveToParentViewController:self];
}
Console says -[__NSArrayM insertObject:AtIndex:] object cannot be nil
I call my init function in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
BottomViewController *bottomVC = [[BottomViewController alloc] initWithNibName:#"BottomViewController" bundle:nil];
TopViewController *topVC = [[TopViewController alloc] initWithNibName:#"TopViewController" bundle:nil];
SlideMenuController *slideController = [[SlideMenuController alloc] initWithBottomViewController:bottomVC andTopViewController:topVC];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = slideController;
[self.window makeKeyAndVisible];
return YES;
}
Properties self.bottomViewController and self.topViewController are declared in .h file:
#property (strong, nonatomic) UIViewController *bottomViewController;
#property (strong, nonatomic) UIViewController *topViewController;
The problem is in the assignment to self.bottomViewController and self.topViewController. bottomViewController and topViewController aren't nil, but self.bottomViewController and self.topViewController are. I can't figure it out by myself. Thanks in advance!
The problem was that I implemented empty setter methods for both self.bottomViewController and self.topViewController.

iOS 6: supportedInterfaceOrientations is called but shouldAutorotate is not called

I know this question has been asked before here iOS 6 shouldAutorotate: is NOT being called. But my scenario is a bit different.
Initially, on application launch I load a viewController like
self.window.rootViewController = self.viewController;
and When this view is loaded I am using a custom tabBar view (inherited from UIView) which has 5 UINavigationController for each tab. The code of this viewController Class is:
\\\ .h File
#protocol tabbarDelegate <NSObject>
-(void)touchBtnAtIndex:(NSInteger)index;
#end
#class tabbarView;
#interface ViewController : UIViewController <tabbarDelegate>
#property(nonatomic,strong) tabbarView *tabbar;
#property(nonatomic,strong) NSArray *arrayViewcontrollers;
#property(nonatomic,strong) UINavigationController * navigation1;
#property(nonatomic,strong) UINavigationController * navigation2;
#property(nonatomic,strong) UINavigationController * navigation3;
#property(nonatomic,strong) UINavigationController * navigation4;
#property(nonatomic,strong) UINavigationController * navigation5;
#end
and m file is
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat orginHeight = self.view.frame.size.height- 60;
_tabbar = [[tabbarView alloc]initWithFrame:CGRectMake(0, orginHeight, 320, 60)];
_tabbar.delegate = self;
[self.view addSubview:_tabbar];
_arrayViewcontrollers = [self getViewcontrollers];
[self touchBtnAtIndex:0];
}
-(void)touchBtnAtIndex:(NSInteger)index \\This delegate method is called on every custom tabBar button clicked.
{
UIView* currentView = [self.view viewWithTag:SELECTED_VIEW_CONTROLLER_TAG];
[currentView removeFromSuperview];
UINavigationController *viewController = [_arrayViewcontrollers objectAtIndex:index];
viewController.view.tag = SELECTED_VIEW_CONTROLLER_TAG;
viewController.view.frame = CGRectMake(0,0,self.view.frame.size.width, self.view.frame.size.height- 50);
[self.view insertSubview:viewController.view belowSubview:_tabbar];
}
-(NSArray *)getViewcontrollers
{
FirstViewController *firstController = [[FirstViewController alloc]initWithNibName:#"FirstViewController" bundle:nil];
SecondViewController *secondController = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
ThirdViewController *thirdController = [[ThirdViewController alloc]initWithNibName:#"ThirdViewController" bundle:nil];
ForthViewController *forthController = [[ForthViewController alloc]initWithNibName:#"ForthViewController" bundle:nil];
FifthViewController *fifthController = [[FifthViewController alloc]initWithNibName:#"FifthViewController" bundle:nil];
navigation1 = [[UINavigationController alloc] initWithRootViewController:firstController];
navigation2 = [[UINavigationController alloc] initWithRootViewController:secondController];
navigation3 = [[UINavigationController alloc] initWithRootViewController:thirdController];
navigation4 = [[UINavigationController alloc] initWithRootViewController:forthController];
navigation5 = [[UINavigationController alloc] initWithRootViewController:fifthController];
NSArray* tabBarItems = [[NSArray alloc] initWithObjects:navigation1,navigation2,navigation3,navigation4,navigation5, nil];
return tabBarItems;
}
I have also implemented UINavigationController's category for rotation as:
#implementation UINavigationController (autoRotation)
-(BOOL)shouldAutorotate {
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations {
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
My Problem is that only supportedInterfaceOrientations method is called and shouldAutorotate is never called. The main Window's rootViewController is ViewController class, not any UINavigationController or UITabBarController. What I am doing wrong? Please help me.
If you add these to your root view controller:
- (BOOL)shouldAutorotate
{
NSLog(#"ViewController shouldAutorotate super=%d", [super shouldAutorotate]);
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
NSLog(#"ViewController supportedInterfaceOrientations");
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
you will see that the system is constantly sending both of these messages. Your code there can query the active Navigation Controller to see what values to return if you wish.
Also, I'm no fan of using a category to override these on a UINavigation Controller - that it works is somewhat of a fluke and importing some other library that does the same could cause unexpected consequences later on. Just create a really simple subclass and over ride both of these. I've been doing that with both UITabBarController and UINavigationController since iOS4 and never had an issue.

Different URI for each instance of UIViewController

I have a UITabController with five tabs. Each tab simply holds an instance of a custom UIViewController, and each instance holds a UIWebView.
I want the UIWebView in each tab to open a different URI, but I don't think it should be necessary to create a new class for each tab.
I can make it work if I do [self.webView loadRequest:] in -(void)viewDidLoad but it seems ridiculous to create five different classes with five different versions of viewDidLoad when all I really want to change is the URI.
Here's what I've tried:
appDelegate.h
#import <UIKit/UIKit.h>
#interface elfAppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UITabBarController *tabBarController;
#end
appDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
customVC *start = [[customVC alloc] init];
customVC *eshop = [[customVC alloc] init];
customVC *table = [[customVC alloc] init];
customVC *video = [[customVC alloc] init];
customVC *other = [[customVC alloc] init];
// Doesn't do anything... I wish it would!
[start.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]]];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = #[start, eshop, table, video, other];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
customVC.h
#import <UIKit/UIKit.h>
#interface customVC : UIViewController <UIWebViewDelegate> {
UIWebView* mWebView;
}
#property (nonatomic, retain) UIWebView* webView;
- (void)updateAddress:(NSURLRequest*)request;
- (void)loadAddress:(id)sender event:(UIEvent*)event;
#end
customVC.m
#interface elfVC ()
#end
#implementation elfVC
#synthesize webView = mWebView;
- (void)viewDidLoad {
[super viewDidLoad];
self.webView = [[UIWebView alloc] init];
[self.webView setFrame:CGRectMake(0, 0, 320, 480)];
self.webView.delegate = self;
self.webView.scalesPageToFit = YES;
[self.view addSubview:self.webView];
}
Create a property of type NSURL* in your customVC.
Change your customVC's -(id)init method to -(id)initWithURL:(NSURL*)url as follows:
-(id)initWithURL:(NSURL*)url{
self = [super init];
if(self){
self.<URLPropertyName> = url;
}
return self;
}
Then call
[start.webView loadRequest:[NSURLRequest requestWithURL:self.<URLPropertyName>]];
in viewDidLoad
Then when you initialize your different instances of customVC, just use
customVC *vc = [customVC alloc]initWithURL:[NSURL URLWithString:#"http://..."]];
I suspect you are initialising your webview after calling it's loadRequest: method. To avoid this, it's a better practice to initialise nonatomic properties by overriding their setters:
- (UIWebView*)webview {
if (mWebView == nil) {
// do the initialization here
}
return mWebView;
}
This way your webview will be initialised when you first access it (while calling loadRequest:) and not after it, in your custom view controller's viewDidLoad method.

second Tab is always greyed out in UITabbarController

I'm trying to embed two NavBar Controllers into a UITabbarController, using the code below in the app delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
[window makeKeyAndVisible];
// Configure and show the window.
FirstViewController *firstController = [[FirstViewController alloc] initWithNibName:nil bundle:NULL];
self.firstNav = [[UINavigationController alloc] initWithRootViewController:firstController];
SecondTableViewController *anotherOne = [[SecondTableViewController alloc] initWithNibName:nil bundle:NULL];
self.anotherNav = [[UINavigationController alloc] initWithRootViewController:anotherOne];
NSArray *twoViewControllers = [[NSArray alloc]initWithObjects:self.anotherNav,self.firstNav, nil];
self.tabBarController = [[UITabBarController alloc]init];
[self.tabBarController setViewControllers:twoViewControllers];
[window addSubview:self.tabBarController.view];
[window setRootViewController:tabBarController];
return YES;
}
The two tabs are displayed fine, with their names, but I can select the first one only, the second one is always greyed out and does not respond to touch events. I reversed the navController assignment into the Tabbar (i.e. in the array twoViewControllers), and each view is displayed fine (in the first tab).
The app delegate does not implement UITabbarDelegate and uiTabBarControllerDelegate. I don't use story boards.
Any obvious reason why the second tab is always greyed out ?
Sample Code:
It's just Apple's Locations tutorial (with ARC)
here
Then change :
(void)applicationDidFinishLaunching:(UIApplication *)application {}
for
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Configure and show the window.
RootViewController *cont1 = [[RootViewController alloc] initWithNibName:nil bundle:NULL];
RootViewController *cont2 = [[RootViewController alloc] initWithNibName:nil bundle:NULL];
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Handle the error.
}
cont1.managedObjectContext = context;
cont2.managedObjectContext = context;
UINavigationController *aNav1 = [[UINavigationController alloc] initWithRootViewController:cont1];
UINavigationController *aNav2 = [[UINavigationController alloc] initWithRootViewController:cont2];
NSArray *twoViewControllers = [[NSArray alloc]initWithObjects:aNav1,aNav2, nil];
self.tabBarController = [[UITabBarController alloc]init];
[self.tabBarController setViewControllers:twoViewControllers];
//[window addSubview:[tabBarController view]];
[window setRootViewController:tabBarController];
[window makeKeyAndVisible];
}
and replace #interface LocationAppDelegate ...#end by
#interface LocationsAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
}
#property (nonatomic, strong) IBOutlet UIWindow *window;
#property (nonatomic, strong) UITabBarController *tabBarController;
- (IBAction)saveAction:sender;
#property (nonatomic, strong, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, strong, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (weak, nonatomic, readonly) NSString *applicationDocumentsDirectory;
#end
and Compile and run.
Try to get rid of [window addSubview:self.tabBarController.view];

Pushing View Controller with Two Nav Controllers

I've got an app where I am pushing a modal view controller. It is working fine, but I am concerned I haven't coded it in the most correct fashion. I have instanstiated two navigation controllers, which seems a bit dodgy to me.
Basically I've created a tab bar controller with 3 tabs, then made one of those tabs / view controllers the root. Later I am (using some home-grown markup on core text) popping a view controller when the user touches a particular word in a paragraph. The pushed view controller has a back button which works fine and the app seems to be OK.
Like I said it all works, but it seems I am coding in circles here. Is this correct?
AppDelegate.h
#import <Foundation/Foundation.h>
#interface AppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{
UIWindow *window;
UITabBarController *tabBarController;
}
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UITabBarController *tabBarController;
#end
From AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
ViewController2 *viewController2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
ViewController3 *viewController3 = [[ViewController3 alloc] initWithNibName:#"ViewController3" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:nav, viewController2, viewController3, nil];
self.tabBarController.delegate = self;
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
ViewController3.h
#import <UIKit/UIKit.h>
#import "JSCoreTextView.h"
#import "PopupViewController.h"
#class JSTwitterCoreTextView;
#interface ReadingViewController : UIViewController <JSCoreTextViewDelegate>
{
JSTwitterCoreTextView *_textView;
UIScrollView *_scrollView;
}
#end
From ViewController3.m
Here I am instantiating another navigation controller. Is this a good idea?
- (void)textView:(JSCoreTextView *)textView linkTapped:(AHMarkedHyperlink *)link
{
PopupViewController *popupVC = [[PopupViewController alloc] initWithNibName:#"PopupViewController" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:popupVC];
[nav setModalPresentationStyle:UIModalPresentationFullScreen];
[nav setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentModalViewController:nav animated:YES];
}
From PopupViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector(done:)]];
}
- (void)done:(id)sender
{
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
It appears the answer is "yes". I was under the impression there is a single Navigation Controller for the app, but it's more like one per tab, depending on if there are going to be further pushes from that tab.

Resources