UIView subview of UIWindow doesn't rotate - ios

I'm adding a UIView to a UIWindow that's not the keyWindow. I'd like the UIView to rotate when the device rotates. Any special properties on the window or the view I need to set? Currently the view is not rotating.
I'm aware of the fact that only the first subview of the application's keyWindow is told about device rotations. As a test I added my view to the first subview of the keyWindow. This causes the view to rotate. However the view being a subview of the keyWindow's first subview won't work for various aesthetic reasons.
An alternative approach is observing device orientation changes in the view and writing the rotation code myself. However I'd like to avoid writing this code if possible (having an additional window is cleaner in my opinion).

UIView doesn't handle rotations, UIViewController does.
So, all you need is to create a UIViewController, which implements shouldAutorotateToInterfaceOrientation and sets this controller as a rootViewController to your UIWindow
Something like that:
UIViewController * vc = [[[MyViewController alloc] init] autorelease];
vc.view.frame = [[UIScreen mainScreen] bounds];
vc.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
//you vc.view initialization here
UIWindow * window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
window.windowLevel = UIWindowLevelStatusBar;
window.backgroundColor = [UIColor clearColor];
[window setRootViewController:vc];
[window makeKeyAndVisible];
and I used this MyViewController, cause I want it to reflect changes of main application
#interface MyViewController : UIViewController
#end
#implementation MyViewController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
UIWindow *window = ((UIWindow *)[[UIApplication sharedApplication].windows objectAtIndex:0]);
UIViewController *controller = window.rootViewController;
if (!controller) {
NSLog(#"%#", #"I would like to get rootViewController of main window");
return YES;
}
return [controller shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}
#end
but you can always just return YES for any orientation or write your logic, if you wish.

You can add below code to your project
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification object:nil];
and created the function to handle it.

Related

Instance of UIView subclass is not full screen even if its frame is the window bounds property

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
CGRect frame = self.window.bounds;
BNRHypnosisView *firstView = [[BNRHypnosisView alloc] initWithFrame:frame];
[self.window addSubview:firstView];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
The instance of BNRHypnsosisView (subclass of UIView), firstView, has its frame equal to window bounds. Why it isn't full screen?
EDIT: In BNRHypnosis View , I had a #property (nonatomic) CGRect frame. That's the only thing I had in this subclass. After I deleted it ( i've seen i wasn't using it anywhere ) , everything worked fine. Can somebody tell me why?
The declared "frame" property override the existing property "frame" of UIView. It is a fundamental property of any UIView in iOS. If you override it, you have to call [super setFrame:<frameValue>] in your implementation to not loose the base and mandatory functionnality.
This declared property is not present in the actual implementation of BNRHypnosisView on GitHub: https://github.com/rahims/iOS-Programming-The-Big-Nerd-Ranch-Guide/blob/master/Chapter-5/Hypnosister/Hypnosister/BNRHypnosisView.h

Laying out views in a TabBarViewController

I'm trying to manually (programmatically) lay out views in a UITabBarViewController. I instantiate my UITabBarViewController like this:
MYAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UITabBarController *tabBarController = [[UITabBarController alloc] initWithNibName:nil bundle:nil];
MYViewController1 *myViewController1 = [[MYViewController1 alloc] init];
myViewController1.title = #"My VC 1";
[tabBarController addChildViewController:myViewController1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = tabBarController;
return YES;
}
MYViewController1.m
- (void)viewDidLoad
{
[super viewDidLoad];
_myView = [[MYView alloc] initWithFrame:self.view.frame];
[self.view addSubview:_myView];
}
MYView.m
- (void)layoutSubviews
{
CGRect descriptionRect, buttonRect;
CGRectDivide(self.frame, &buttonRect, &descriptionRect, 50.f, CGRectMaxYEdge);
_descriptionTextView.frame = descriptionRect;
[self addSubview:_descriptionTextView];
_myButton.frame = buttonRect;
[self addSubview:_myButton];
}
The problem I'm having is that when I get to layoutSubviews, the superview's frame is the full size of the window, so the button is hidden by the tab bar. What am I doing wrong?
Without seeing more code, it appears that you may have some confusion about the use of a UITabBarController.
In the code you have posted above you are initializing a UITabBarController, then a UIViewController, and then you are calling 'addChildViewController:' on the tabBarController. (however, addChildViewController: is an instance method on UIViewController).
Is this in attempt to add the ViewController as a tab of the TabBarController? If so, then try the following code in place of addChildViewController: to see if it gives you the functionality that you are looking for:
tabBarController.viewControllers = #[myViewController1]; // Passing an array of viewControllers will set them as a tabs on the TabBar in the order they are added to the array.
If that doesn't help, then please comment on this answer with more details regarding the desired functionality of your code, and I'll update my answer to assist you as much as I can.
EDIT: Looks like I may have misunderstood your problem. Can you try the following code in play of your subview's initialization:
_myView = [[MYView alloc] initWithFrame:self.view.bounds]; // Try bounds instead of frame.

viewDidLoad is not called for rootViewController

This is my first iOS project. I'm just following the tutorial from Try iOS over at codeschool in my XCode Application.
I've added a button in my viewDidLoad method. I don't think this is where it would be added normally, but it should still work. The problem is, the method is never called. Here's my code so far:
AppDelegate.m:
#import "AppDelegate.h"
#import "ViewController.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Set window size & background color
CGRect viewRect = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:viewRect];
self.viewController = [[ViewController alloc] init];
UIView *view = [[UIView alloc] initWithFrame:viewRect];
view.backgroundColor = [UIColor yellowColor];
self.viewController.view = view;
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
NSLog(#"Screen is %f tall and %f wide", viewRect.size.height, viewRect.size.width);
return YES;
}...
AppDelegate.h:
#import <UIKit/UIKit.h>
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
#end
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
UIButton *firstButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
firstButton.frame = CGRectMake(100, 100, 100, 44);
[firstButton setTitle:#"Don't Click!" forState:UIControlStateNormal];
[self.view addSubview:firstButton];
}...
When the app is up and running, my background remains yellow, and there is no button visible.
Stepping through the application, didFinishLaunchingWithOptions: hits as I expected. When the view is initializes, I expect the viewDidLoad to run. I may just be misunderstanding the way C "sends messages".
If any additional information might help please let me know. Thanks for any help :)
EDIT
Does this have something to do with storyboarding? I'm in storyboard mode but I never added any button using the GUI
viewDidLoad is not being called because of the non-standard way that you're creating its view. Usually, the view is loaded from a xib or storyboard, or created in code in the loadView method of the view controller -- in all these cases, viewDidLoad will be called. If you move your view creation to loadView in the controller's .m file, it will work, and viewDidLoad will be called.
After Edit:
If you're using a storyboard, your view should be created there, and you shouldn't be doing what you're doing in the app delegate. In fact, when you use a storyboard, you don't normally have any code in the application:didFinishLaunchingWithOptions: method.
...
self.viewController.view = view;
...
You shouldn't set viewController's view before viewDidLoad, which cause viewDidLoad not called. View should be set in viewDidLoad, and the viewController's will manage its view automatically for you.
Looking at the code, this is exactly what should be happening. When you create a viewController, it automatically creates a view so you do not want to create a new one for it. Your button is not showing up because it is on the original view created with the viewController but you overwrote it with your yellow view.
Instead what you want to do is go into ViewController.m's viewDidLoad:(BOOL)animated and add:
[self.view setBackgroundColor:[UIColor yellowColor]];
Also, make sure that you do not set viewController.view in appDelegate. If you do not set that, a view is automatically created and you can add your button or any other views to it in the viewDidLoad.
I had this issue, and the initWithCoder function was called when I added some code in the AppDelegate's didFinishLaunchingWithOptions function.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];(NSDictionary *)launchOptions
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MyStoryboardName" bundle:nil];
UIViewController *initViewController = [storyboard instantiateInitialViewController];
[self.window setRootViewController:initViewController];

Modal viewcontroller UI not responsive after presentViewController:animated:completion:

My app has a root viewcontroller, which at the start of the app displays
login viewController view if the user is not logged in
main viewController view if the user is logged in
AppDelegate code:
- (BOOL) application: (UIApplication*) application
didFinishLaunchingWithOptions: (NSDictionary*) launchOptions
{
self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.rootViewController = [[RootViewController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
Here's the code used in RootViewController:
#implementation RootViewController
- (void) loadView
{
[super loadView];
// here mainViewController and loginNavigationController are initialized
}
...
- (UIView*) view
{
[super view]; // this invokes loadView
return self.isLoggedIn ? self.mainViewController.view :
self.loginNavigationController.view;
}
....
- (void) userDidLogin
{
[self.loginNavigationController presentViewController: self.mainViewController
animated: YES
completion: nil];
}
#end
If the user is not logged in and presses login button the main viewController is presented.
The problem is that after main viewController is presented, I'm not able to interact with any of the UI elements. For example, I have a tableView as a main viewController's subview and when I try to scroll it I get the following warning in debug panel:
<UITableView: 0x202a4000; frame = (0 0; 310 548); clipsToBounds = YES;
gestureRecognizers = <NSArray: 0x1fd9f570>; layer = <CALayer: 0x1fdccff0>;
contentOffset: {0, 0}>'s window
is not equal to <RootViewController: 0x1fd9f7d0>'s view's window!
Ok, so after looking at the updated code I see that you have a rootViewController and are dynamically giving the view you think should be presented. The thing is, the rootViewController is in charge of the root view while your other two view controllers manage their own views. You should not be passing a different view controller's view off.
So in the end it looks like you want to conditionally set your rootviewcontroller. So lets look at the app delegate. I think you should make your app delegate do something like this. Have it figure out at runtime which viewcontroller to present. Then make that the rootviewcontroller for the app.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController * resolvedRootViewController = [self someMethodThatCorrectlyGivesRootViewController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = resolvedRootViewController;
[self.window makeKeyAndVisible];
return YES;
}

UIView doesn't become First Responder when parent UIViewController is set as RootViewController

I'm working on Chapter 7 of BNR's iOS Programming book and I've run into a problem. At the start of the chapter I setup a UIViewController (HypnosisViewController) with an UIView (HypnosisView) that responded to motion events in the previous chapter.
I create the UIViewController in the AppDelegate.m file:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
HypnosisViewController *hvc = [[HypnosisViewController alloc] init];
[[self window] setRootViewController:hvc];
...
}
In the HypnosisViewController, I set HypnosisView to become first responder:
- (void)loadView
{
// Create a view
CGRect frame = [[UIScreen mainScreen] bounds];
HypnosisView *view = [[HypnosisView alloc] initWithFrame:frame];
[self setView:view];
[view becomeFirstResponder];
}
And in HypnosisView I make sure to return YES to canBecomeFirstResponder. Unfortunately, the HypnosisView did not respond to motion events like before. When I eventually moved on, I made an interesting discovery. If I move HypnosisViewController into a UITabBarController, HypnosisView starts responding to motion events. The code looks something like this:
HypnosisViewController *hvc = [[HypnosisViewController alloc] init];
UITabBarController *tabBarController = [[UITabBarController alloc] init];
NSArray *viewControllers = [NSArray arrayWithObjects:hvc, <insert more objs here>, nil];
[tabBarController setViewControllers:viewControllers];
[[self window] setRootViewController:tabBarController];
Why didn't HypnosisView become first responder when HypnosisViewController was set as the RootViewController? Why did it start working once HypnosisViewController was placed inside another controller? What am I missing about RootViewController?
Thanks!
Your question is very apt. I'm also studying the same book and am on the same chapter. The thing is that before we used UITabBarController we would either use HypnosisViewController or TimeViewController. And we would then do [self.window setRootViewController:hvc] or [self.window setRootViewController:tvc] in the AppDelegate.m file. In that case setRootViewController method was calling loadView method internally. So if loadView should get called then becomeFirstResponder (which resides inside of it as a method call as per your code) also is supposed to get triggered. So internally canBecomeFirstResponder should get called
Now when we use UITabBarController, things tend to break. What happens is instead of loadView getting called via '[[self window] setRootViewController:tabBarController];' line of code, it gets called through '[tabBarController setViewControllers:viewControllers];'. So the bottomline is that rootViewController property (when set to tabBarController) does not call loadView method and hence 'becomeFirstResponder' is not called. You may argue that loadView does get called through '[tabBarController setViewControllers:viewControllers];' but setViewControllers is not used for setting root viewController.
When I faced this problem, I made an explicit call to becomeFirstResponder. Here's how:-
#implementation HypnoTimeAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions //method of UIApplicationDelegate protocol
{
NSLog(#"lets begin");
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
HypnosisViewController *viewController= [[HypnosisViewController alloc] init];
TimeViewController *viewController2= [[TimeViewController alloc] init];
NSLog(#"view controllers are done initializing!");
UITabBarController *tabBarController= [[UITabBarController alloc] init];
NSArray *viewControllers= [NSArray arrayWithObjects:viewController,viewController2, nil];
[tabBarController setViewControllers:viewControllers];//loadView of HypnosisViewController gets called internally since the 'app view' isn't going to load from a XIB file but from 'HypnosisView.m'.loadView method of TimeViewController loads its own view from the XIB file.
[self.window setRootViewController:tabBarController];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
#implementation HypnosisViewController
-(void)loadView{
NSLog(#"HypnosisView loading...");
HypnosisView *myView= [[HypnosisView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view= myView;
[self configureFirstResponder];//configuring first responder
}
-(void) configureFirstResponder{
BOOL viewDidBecomeFirstResponder= [self.view becomeFirstResponder];
NSLog(#"Is First Responder set as HypnosisView? %i",viewDidBecomeFirstResponder);
}

Resources