ModalViewController for Single Subview - objective-c

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.

Related

Executing ViewController's methods from UINavigationController

I have created several ViewControllers in a storyboard that each have their own class files. In AppDelegate I have programatically generated a UINavigationController that exists at the top of the app for every page. This will have two buttons that will be the same for every ViewController, one will load a ViewController called 'settings' and one will fire a method that reveals a side menu.
Screen shots to illustrate:
Currently, each ViewController has a button in the top left, that when pressed moves the current ViewController across revealing the menu below.
This works fine but what I want is for this button to be removed and replaced with the button that is on the NavigationController (currently place holder menu button seen in the purple NavigationController).
How do I implement the code that moves the ViewController in the AppDelegate, what the UINavigationController is generated?
AppDelegate.m
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
MainViewController* mainVC = [mainStoryboard instantiateInitialViewController];
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:mainVC];
[mainVC setTitle:#"Congress app"];
UIBarButtonItem *showSettingsButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:#selector(showSettings:)];
UIBarButtonItem *showMenuButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"menuButton.png"] style:UIBarButtonItemStylePlain target:self action:#selector(revealMenu:)];
mainVC.navigationItem.leftBarButtonItem = showMenuButton;
mainVC.navigationItem.rightBarButtonItem = showSettingsButton;
[self.window setRootViewController:navVC];
[_window makeKeyAndVisible];
return YES;
}
- (IBAction)revealMenu:(id)sender
{
// This obviously won't work but what should go here instead?
// Something like get instance of MainViewController and fire it's reveal menu
// method but passing the current ViewController Id and running slidingViewController anchorTopViewTo:ECRight on that?
[self.slidingViewController anchorTopViewTo:ECRight];
}
MainViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) {
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MenuVC"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_menuBtn.frame = CGRectMake(8, 80, 34, 24);
[_menuBtn setBackgroundImage:[UIImage imageNamed:#"menuButton.png"] forState:UIControlStateNormal];
[_menuBtn addTarget:self action:#selector(revealMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
NSLog(#"MainVC loaded");
}
- (IBAction)revealMenu:(id)sender
{
[self.slidingViewController anchorTopViewTo:ECRight];
}
You're better off not doing that...
This isn't app delegate responsibility
There are 3rd party implementations on github / CocoaControls which offer this and manage the navigation bar
It is much better to rework your current view hierarchy than to force a connection from the app delegate.
The responsibility of the app delegate is to respond to app level events (like foreground / background notifications). It might be involved in setting up the initial UI but other than that is should do basically nothing.

Initiate self.window in appDelegate init method

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.

Reading touch events in a QLPreviewController

I've got a QuickLook view that I view some of my app's documents in. It works fine, but I'm having my share of trouble closing the view again. How do I create a touch event / gesture recognizer for which I can detect when the user wants to close the view?
I tried the following, but no events seem to trigger when I test it.
/------------------------ [ TouchPreviewController.h ]---------------------------
#import <Quicklook/Quicklook.h>
#interface TouchPreviewController : QLPreviewController
#end
//------------------------ [ TouchPreviewController.m ]---------------------------
#import "TouchPreviewController.h"
#implementation TouchPreviewController
- (id)init:(CGRect)aRect {
if (self = [super init]) {
// We set it here directly for convenience
// As by default for a UIImageView it is set to NO
UITapGestureRecognizer *singleFingerDTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleDoubleTap:)];
singleFingerDTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:singleFingerDTap];
[self.view setUserInteractionEnabled:YES];
[self.view setMultipleTouchEnabled:YES];
//[singleFingerDTap release];
}
return self;
}
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *) sender {
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[UIView beginAnimations:nil context:NULL];
sender.view.center = tapPoint;
[UIView commitAnimations];
NSLog(#"TouchPreviewController tap!" ) ;
}
// I also tried adding this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherGestureRecognizer {
return YES;
}
#end
Edit: For clarification, this is how I instantiate the controller:
documents = [[NSArray alloc] initWithObjects: filename , nil ] ;
preview = [[TouchPreviewController alloc] init];
preview.dataSource = self;
preview.delegate = self;
//set the frame from the parent view
CGFloat w= backgroundViewHolder.frame.size.width;
CGFloat h= backgroundViewHolder.frame.size.height;
preview.view.frame = CGRectMake(0, 0,w, h);
//refresh the preview controller
[preview reloadData];
[[preview view] setNeedsLayout];
[[preview view] setNeedsDisplay];
[preview refreshCurrentPreviewItem];
//add it
[quickLookView addSubview:preview.view];
Also, I've defined the callback methods as this:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return [documents count];
}
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index
{
return [NSURL fileURLWithPath:[documents objectAtIndex:index]];
}
Edit2: One thing i noticed. If I try making swiping gestures, I get the following message. This could shed some light on what is wrong/missing?
Ignoring call to [UIPanGestureRecognizer setTranslation:inView:] since
gesture recognizer is not active.
I think your example code is incomplete. It isn't clear how you are instantiating the TouchPreviewController (storyboard, nib file or loadView.)
I have never used the class so I could be way out in left field.
If you've already instantiated a UITapGestureRecognizer in the parent viewController, it is absorbing the tap events and they aren't passed on to your TouchPreviewController.
I would implement the view hierarchy differently by attaching the UITapGestureRecognizer to the parent viewController and handle presentation and unloading of the QLPreviewController there.
I think you might not have to subclass QLPreviewController by instantiating the viewController from a nib file.
When your parent viewController's UITapGestureRecognizer got an event you would either push the QLPreviewController on the navigation stack or pop it off the navigation stack when done.
Hope this is of some help.

Seamlessly flip from one modal view to another, without showing a solid colour background

I have the following UI for an iPad app:
When I click on the Settings button, I want the dialog to horizontally flip to show the settings dialog.
I have this working fine. But, there is a background colour shown when the dailog flips over. As you can see:
Is there any way to not have this block of colour be visible as the dialogs flip? I'd like it to look more seamless -- as if it's a sheet of paper flipping over.
The views are essentially this:
Window
Main View. Set to the window's rootViewController
Login modal view
Thus the main window and root controller are setup as follows (in the app delegate class):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
The login window is setup and shown in the main view's viewDidAppear:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Setup and show Login dialog
LoginViewController* controller = [[LoginViewController alloc] initWithNibName:#"LoginView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
controller.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:controller animated:YES];
}
And when the Settings button is pressed: showing the Settings modal view is done in pretty much the same way that the Login modal view was shown:
- (IBAction)settingsButtonPressed:(id)sender {
SettingsViewController *controller = [[SettingsViewController alloc] initWithNibName:#"SettingsView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
controller.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:controller animated:YES];
}
I don't think there's any way to do what you want using the modalPresentationStyle. You'll need to implement the animation yourself using a transition animation using the following method:
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
With the UIViewAnimationOptionTransitionFlipFromLeft option.
In this case the new view you want to flip is not the content of the modal (the controller.view) but the modal frame itself, so experiment with just calling the method above from your settings button, and instead of passing controller.view, substitute controller.view.superview, and if that doesn't work, try controller.view.superview.superview until the animation looks right.
It will require some tweaking to work out exactly how to do it.

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.