Initiate self.window in appDelegate init method - objective-c

I'm a web developer creating an Apache Cordova application so my knowledge with Objective-C is very little. Everything is going fine until i try to supplement the splash screen with a video. It sort of does it, but not fully.. It starts with displaying the Default.png followed by the SplashScreenLoader. It then actually plays the video and I know this because the audio is emitted, but the video layer isn't shown.
What I've found out is that the self.window or self.viewController are both defined in didFinishLaunchingWithOptions, so they don't exist in the - (id) init method. Therefore I can't find a way to place it on top of the loading splash.
My init method currently looks like this in AppDelegate.m:
- (id) init {
NSString *moviePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Splash_v1.mp4"];
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
MPMoviePlayerController* moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL: movieURL];
moviePlayer.controlStyle = MPMovieControlStyleNone;
[moviePlayer.view setFrame: self.window.bounds];
[self.window addSubview:moviePlayer.view];
[self.window makeKeyAndVisible];
[moviePlayer play];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
[cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
[CDVURLProtocol registerURLProtocol];
return [super init];
}
Here the self.window is null, and I've also attempted to set the self.window with this code:
CGRect screenBounds = [[UIScreen mainScreen] bounds];
self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease];
...without prevail. It actually sets it, but for the subsequent code it doesn't wanna do it.
So what I'm wondering is, how would I place this video on top of the splash's content, before didFinishLaunchingWithOptions kicks in?
Thanks in advance,
//Peter

So what I'm wondering is, how would I place this video on top of the splash's content, before didFinishLaunchingWithOptions kicks in?
actually, you do that in didFinishLaunchingWithOptions. put this statements in there (in the bolierplate code that Xcode generates for you, you should already have a call to makeKeyAndVisible, so just complement it):
[self.window addSubview:moviePlayer.view];
[self.window makeKeyAndVisible];
having previously instantiated your moviePlayer.
One way to avoid the blank screen could be this:
create a UIImageView containing your Default.png image;
display such UIImageView by adding it to your self.window as a subview (this will create no black screen effect);
initialize your player (I assume it takes some time, hence the black screen) and add it below the UIImageView;
when the player is ready (viewDidLoad) push it on top of the UIImageView.
Finally, I don't know how your player will signal the end of the video play, but I assume you have some delegate method; make you appDelegate be also your player delegate and from there, remove UIImageView and player from self.window and add you other view to self.window.
Hope this helps.
EDIT:
This is a rough sketch of what I would try and do in your app delegate appDidFinishLaunching:
self.moviePlayer = <INIT MOVIEW PLAYER CONTROLLER>
self.backgroundImage = <INIT UIImageView with Default.png>
[self.window addSubview:self.moviePlayer.view];
[self.window addSubview:self.backgroundImage];
[self.window makeKeyAndVisible];
In your movie Player viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
...
[self.view.superview addSubview:self.view]; //-- this will just move the player view to the top
...
}

If that helps anyone else, I had the same issue which was resolved by moving '[self.window makeKeyAndVisible];' above the subview like this, so in appdelegate.m:
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
myView.backgroundColor = [UIColor blueColor];
[self.window makeKeyAndVisible];
[self.window addSubview:myView];
This works fine, but if I swap the last two statements around, the subview does not display.

Related

iOS10 SFSafariViewController not working when alpha is set to 0

I'm using SFSafariViewController to grab user's cookie in my app. Here's is my code:
SFSafariViewController *safari = [[SFSafariViewController alloc]initWithURL:[NSURL URLWithString:referrerUrl] entersReaderIfAvailable:NO];
safari.delegate = self;
safari.modalPresentationStyle = UIModalPresentationOverCurrentContext;
safari.view.alpha = 0.0;
safari.view.hidden = true;
[self dismissViewControllerAnimated:false completion:nil];
NSLog(#"[referrerService - StoreViewController] presenting safari VC");
[self presentViewController:safari animated:false completion:nil];
This works well on iOS 9. but on iOS 10 it seems that the SF controller doesn't work (it also block my current context - which happens to be another UIWebView).
Anyone can suggest of an alternative way to hide a SFSafariViewController?
Updated answer:
Apple prohibits this kind of SafariViewController usage in last version of review guidelines:
SafariViewContoller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers. Additionally, an app may not use SafariViewController to track users without their knowledge and consent.
Old answer:
In iOS 10 there are some additional requirements for presented SFSafariViewController:
1) Your view should not be hidden, so hidden should be set to NO
2) The minimum value for alpha is 0.05
3) You need to add controller manually with addChildViewController: / didMoveToParentViewController: (callback's doesn't called otherwise).
4) UIApplication.keyWindow.frame and SFSafariViewController.view.frame should have non-empty intersection (in appropriate coordinate space), that means:
safari view size should be greater than CGSizeZero
you can't place safari view off the screen
but you can hide safari view under your own view
Code example:
self.safariVC = [[SFSafariViewController alloc] initWithURL:referrerUrl];
self.safariVC.delegate = self;
self.safariVC.view.alpha = 0.05;
[self addChildViewController:self.safariVC];
self.safariVC.view.frame = CGRectMake(0.0, 0.0, 0.5, 0.5);
[self.view insertSubview:self.safariVC.view atIndex:0];
[self.safariVC didMoveToParentViewController:self];
Also, don't forget to remove safariVC properly after the end of the usage:
[self.safariVC willMoveToParentViewController:nil];
[self.safariVC.view removeFromSuperview];
[self.safariVC removeFromParentViewController];
New info: Don't do this.
The revised App Store guidelines prohibit this practice.
https://developer.apple.com/app-store/review/guidelines/
I'll leave this below for posterity:
I call the following code from my app delegate's didFinishLaunchingWithOptions.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self checkCookie];
}
- (void)checkCookie
{
NSURL *url = [NSURL URLWithString:#"http://domainToCheck.com"];
SFSafariViewController *vc = [[SFSafariViewController alloc] initWithURL:url];
vc.delegate = self;
UIViewController *windowRootController = [[UIViewController alloc] init];
self.secondWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.secondWindow.rootViewController = windowRootController;
[self.secondWindow makeKeyAndVisible];
[self.secondWindow setAlpha:0.1];
[windowRootController presentViewController:vc animated:NO completion:nil];
self.window.windowLevel = 10;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(nonnull NSDictionary<NSString *,id> *)options
{
[self.secondWindow.rootViewController dismissViewControllerAnimated:NO completion:nil];
self.secondWindow = nil;
self.window.windowLevel = 0;
return YES;
}
I was able to achieve this affect using invisible child view controller:
_safariViewController = [[SFSafariViewController alloc] initWithURL:_requestURL];
_safariViewController.delegate = self;
_safariViewController.view.userInteractionEnabled = NO;
_safariViewController.view.alpha = 0.0;
[_containerViewController addChildViewController:_safariViewController];
[_containerViewController.view addSubview:_safariViewController.view];
[_safariViewController didMoveToParentViewController:_containerViewController];
_safariViewController.view.frame = CGRectZero;
its possible to use this on swift 3 for xcode 8.3.2 becausa i get all i delegate anduse correctly , but i have this error
[12:05]
2017-04-25 12:05:03.680 pruebaslibrary[7476:136239] Warning: Attempt to present on whose view is not in the window hierarchy!

Black bar between navigation bar and table view appears on iOS 6

I seem to be having an issue since I started using iOS 6, which doesn't appear when using iOS 5. At first, I thought it might just be a simulator bug, but since testing it on my iPhone 5 today, I can see that it's not just in the simulator.
I'm creating everything programmatically — I seem to prefer doing it that way (I assume it's because of my HTML/CSS background!) — but I'm still reasonably new to Objective-C, and I couldn't find a full example of how to set up a navigation controller/table view programmatically, so I put it together from the nuggets of information I could find, and therefore, I could be doing something fundamentally wrong. However, it's worked (and still works) perfectly on iOS 5.
The problem is that I have a black bar between the navigation bar and the table view, but the strange thing is that if I push a view and go back to that original view, the bar disappears, and doesn't reappear until I completely restart the app.
The following image is of the app at launch (1), as I'm pushing a view in (2), and the initial view, after I've gone back to it (3):
This is what I have as my code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController *rootController = [[RootViewController alloc] init];
self.window.rootViewController = rootController;
self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootController];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
NSLog(#"Window frame: %#", NSStringFromCGRect(self.window.frame));
return YES;
}
RootViewController.m
- (void)loadView
{
self.title = #"Title";
self.tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] bounds] style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.view = self.tableView;
NSLog(#"tableView frame: %#", NSStringFromCGRect(self.tableView.frame));
UIBarButtonItem *newViewButton = [[UIBarButtonItem alloc] initWithTitle:#"New View"
style:UIBarButtonItemStylePlain
target:self
action:#selector(newViewButtonTapped:)];
[self.navigationItem setRightBarButtonItem:newViewButton animated:NO];
}
I added the NSLogs to see if they showed anything that might help me. The output is:
tableView frame: {{0, 0}, {320, 480}}
Window frame: {{0, 0}, {320, 480}}
Can anyone give me any ideas about what I'm doing wrong? It seems is having a similar/the same problem (Black bar overtop navigation bar — in the comments), but I haven't found an answer.
Thanks, in advance!
You're adding the RootViewController to the window twice, once by setting UIWindow.rootViewController (which internally does [window addSubview:rootViewController.view]) and again by adding it as a subview of the navigation controller.
You should be doing this:
RootViewController *rootController = [[RootViewController alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootController];
self.window.rootViewController = navigationController;
As a rule of thumb, never add a view directly to the window unless you know that you actually want to.

ViewController view starts with at ~(0,20)

I've tried starting view based app and window based app but I still have the same problem.
When I add a navigation bar it looks like it starts at cords ~(0,20) and leaves some space between the it and the "system tray" (i can't find the word for the dock with clock and batteri etc.)
i can't figure out why this occurs because the nslog of frame tells me that it origins in (0,0) (and strangely the width is 0 :/ but it is clearly visible).
code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(self.view.frame.origin.x,
self.view.frame.origin.y+48.0f,
self.view.frame.size.width,
self.view.frame.size.height)];
[image setImage:[UIImage imageNamed:#"IMG_0792.PNG"]];
[self.view addSubview:image];
self.navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(self.view.frame.origin.x,
self.view.frame.origin.y,
self.view.frame.size.width,
45.0f)];
[navBar setDelegate:self];
[navBar pushNavigationItem: [[UINavigationItem alloc] initWithTitle:#"Image Stream"] animated:NO];
[self.view addSubview:navBar];
}
return self;
}
and in the appdelegate i just declare it, retain-property and:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.menu = [[MenuViewController alloc] initWithNibName:nil bundle:nil];
[self.window setRootViewController:menu];
[self.window makeKeyAndVisible];
return YES;
}
would be very nice if someone knew why this happens. thanks
When positioning a view using the coordinates of the superview, use bounds, not frame. If there's a status bar, your superview.frame.x will be non-zero, but the subview's coordinates are relative to the superview, not the position.
Check the following:
In your UIViewController's Xib if it has status bar. If yes, then put None. (You can set that in the inspector, just select your root UIView.
Are you adding your UIViewController to your App delegate's by XIB? If yes, go check in the App's delegate xib your UIViewController and see if it has status bar.
p.s: its called status bar.
Edit what about the frame you use for your UINavigation? The 45.0f.

ModalViewController for Single Subview

Ok, so bear with me: as this is an Objective-C related question, there's obviously a lot of code and subclassing. So here's my issue. Right now, I've got an iPad app that programmatically creates a button and two colored UIViews. These colored UIViews are controlled by SubViewControllers, and the entire thing is in a UIView controlled by a MainViewController. (i.e. MainViewController = [UIButton, SubViewController, SubViewController])
Now, all of this happens as it should, and I end up with what I expect (below):
However, when I click the button, and the console shows "flipSubView1", nothing happens. No modal view gets shown, and no errors occur. Just nothing. What I expect is that either subView1 or the entire view will flip horizontally and show subView3. Is there some code that I'm missing that would cause that to happen / is there some bug that I'm overlooking?
viewtoolsAppDelegate.m
#implementation viewtoolsAppDelegate
#synthesize window = _window;
#synthesize mvc;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
mvc = [[MainViewController alloc] initWithFrame:self.window.frame];
[self.window addSubview:mvc.theView];
[self.window makeKeyAndVisible];
return YES;
}
MainViewController.m
#implementation MainViewController
#synthesize theView;
#synthesize subView1, subView2, subView3;
- (id)initWithFrame:(CGRect) frame
{
theView = [[UIView alloc] initWithFrame:frame];
CGRect sV1Rect = CGRectMake(frame.origin.x+44, frame.origin.y, frame.size.width-44, frame.size.height/2);
CGRect sV2Rect = CGRectMake(frame.origin.x+44, frame.origin.y+frame.size.height/2, frame.size.width-44, frame.size.height/2);
subView1 = [[SubViewController alloc] initWithFrame:sV1Rect andColor:[UIColor blueColor]];
subView2 = [[SubViewController alloc] initWithFrame:sV2Rect andColor:[UIColor greenColor]];
subView3 = [[SubViewController alloc] initWithFrame:sV1Rect andColor:[UIColor redColor]];
[theView addSubview:subView1.theView];
[theView addSubview:subView2.theView];
UIButton *aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton addTarget:self action:#selector(flipSubView1:) forControlEvents:(UIControlEvents)UIControlEventTouchDown];
[aButton setFrame:CGRectMake(0, 0, 44, frame.size.height)];
[theView addSubview:aButton];
return self;
}
- (void)flipSubView1:(id) sender
{
NSLog(#"flipSubView1");
[subView3 setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[subView1 presentModalViewController:subView3 animated:YES];
}
SubViewController.m
#implementation SubViewController
#synthesize theView;
- (id)initWithFrame:(CGRect)frame andColor:(UIColor *)color
{
theView = [[UIView alloc] initWithFrame:frame];
theView.backgroundColor = color;
return self;
}
TLDR: modal view not working. should see flip. don't.
It doesn't look like you're setting the 'view' property of the MainViewController anywhere, just 'theView'. The controllers view delegate must be connected to the root view it displays for it to work properly. You'll need to correct that on the Sub View Controller impl as well. If you want all the plumbing that framework classes bring, you have to set things up the way they expect.
Also, you're calling presentModalViewController on one of the sub view controllers; change that to call [self presentModalViewController:...], since the MainViewController is the one which will 'own' the modal view.
I think if you fix those points, you'll find -presentModalViewController will work.

Multiple MPMoviePlayerViewController fullscreen movie init & switching issue - Fullscreen View doesn't autorotate and minor similar issues

This is my first post at SO. I would like to thank all of the SO users, their contribution have helped me a lot in all of my projects. I am new to the iPhone Application development scene.
Aim of the project is to have a custom interactive User Interface (UI) on top of Movie Player objects (*.mov files on device). The underlying FullScreen MoviePlayers can be more than one and need to switch to different ones based on user Interaction.
I am using cocos2D to achieve the custom interactive UI and effects (for e.g. particle effects on swipe gesture). And I am using Multiple MPMoviePlayerViewController* to play the resident movie files (*.mov) on fullscreen mode (refered to MoviePlayer sample).
Requirement is to switch between movies by switching between default and alternate moviePlayer upon touch detection. Also, the switching should be as smooth as possible.
To achieve smooth switching I am using two moviePlayerViewControllers, each a subview of UIView object in AppController.
Problem a: I am not sure if this is the right way to achieve movie switching.
Problem b: In my current solution. The Default MoviePlayer is rotated in portrait mode, and sometimes the moviePlayer object is not visible. The behaviour is not consistent.
Problem c: The Alternate movie player (object of MPMoviePlayerViewController and added first as subview of UIWindow) rotates on device rotation and behaves properly the Default Movie Player (object of MPMoviePlayerViewController added after) doesn't. Can't think of logical explanation of it. Somewhere I read that MPMoviePlayerViewController creates it's own window and that might be an issue.
// From AppController.h
UIWindow *window; // Parent application window
UIView *viewCocos2D; // Cocos View
UIView *viewMovie; // Default MoviePlayer View
UIView *viewMovieAlternate; // Alternate MoviePlayer View
// From AppController.m
// From DidAppFinishLoading
{
...
// Init the window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Init the views (Cocos2d, MOviePlayer and AlternateMOviePlayer)
viewCocos2D = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
viewMovie = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
viewMovieAlternate = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// cocos2d will inherit these values
[viewCocos2D setUserInteractionEnabled:YES];
[viewCocos2D setMultipleTouchEnabled:YES];
// create OpenGL view and attach it to a window
//[[[UIApplication sharedApplication] keyWindow] addSubview:window];
[[CCDirector sharedDirector] attachInView:viewCocos2D];
// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// Create a
customMoviePlayerWithUI = [[CustomMoviePlayerWithUI alloc] initWithSuperView:window andMovieView:viewMovie andAlternateView:viewMovieAlternate andSelf:self];
CCScene *scene = [CCScene node];
CocosCustomLayer *cocosLayer = [customMoviePlayerWithUI cocosLayer];
[window addSubview:viewMovieAlternate];
[window addSubview:viewMovie];
// somehow this order lets cocos2D receive the touch events
[window addSubview:viewCocos2D];
[scene addChild: cocosLayer];
[window makeKeyAndVisible];
[[CCDirector sharedDirector] runWithScene: scene];
[UIAccelerometer sharedAccelerometer].delegate = self;
[customMoviePlayerWithUI start];
[self didRotate:nil];
...
}
So the View hierarchy is
Alternate Movie Player View is at bottom
Default Movie Player view is next
Cocos2D Layer View is on top -- Cocos2D view also receives all of the events.
// From CustomMoviePlayerWithUI.h
MPMoviePlayerViewController *moviePlayerView;
MPMoviePlayerViewController *moviePlayerViewAlternate;
// These delegates are used frequently to invoke some functions
AppController* delegateApplication;
UIView *delegateSuperWindow; // Window which contains everything -
UIView *delegateView; // View containing default MoviePlayer
UIView *delegateViewAlternate;// View containing alternate MoviePlayer
Following is the moviePlayerViewController init code -- basically it allocs and inits two moviePlayerViewController objects and adds them as subview to the Default and Alternate UIView objects from AppController
// From customMoviePlayer.m
// initAndPlayMovie is called from the init of CustomMoviePlayer
- (void)initAndPlayMovie:(UIView *)view andAlternateView:(UIView*) viewMovieAlternate
{
// Initialize a movie player object with the specified URL
moviePlayerView = [[MPMoviePlayerViewController alloc] init];
moviePlayerViewAlternate = [[MPMoviePlayerViewController alloc] init];
[moviePlayerView shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft];
[moviePlayerViewAlternate shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft];
//if (moviePlayer)
if (moviePlayerView && moviePlayerViewAlternate)
{
[[[moviePlayerView moviePlayer] backgroundView] setBackgroundColor:[UIColor blueColor]];
[[[moviePlayerViewAlternate moviePlayer] backgroundView] setBackgroundColor:[UIColor redColor]];
//[moviePlayerView setWantsFullScreenLayout:YES];
// private API call.. don't use it..
//[mp setOrientation:UIDeviceOrientationPortrait animated:NO];
[view addSubview:[moviePlayerView view]];
[viewMovieAlternate addSubview:[moviePlayerViewAlternate view]];
//[view bringSubviewToFront:[moviePlayerView view]];
//[[moviePlayerView moviePlayer] play];
}
}
Thanks for all of your help. Let me know if you need any more details on it.