iphone UIWebView, Landscape, Zoom! - iphone

I have a UIWebView in my app and I am having trouble making it work correctly simultaneously with landscape and the viewport zoom.
If I load pages in portrait and then rotate the phone, using the autoresize, it works correctly, zooming in for pages that are set to zoom. However, if I start the webview in landscape mode, I have to rotate to portrait and then rotate back to landscape to get the zoom correct. Mobile Safari does not have this problem.
For example if you load mobile.washingtonpost.com in the webview starting in landscape mode, the text is small. If you rotate to portrait and then back to landscape, the text is larger (as it should be). Mobile safari is smart enough to have the text large immediately when you load it in landscape. I want that :)
I have a workaround, but it is a total hack and I am wondering if there is a better way. Basically, I can get the landscape to have the right zoom, but only after it is loaded by twiddling the frame size of the webivew to 320 wide and then back to 480 wide. This gets me there, but: A) It seems silly, B) I can't do it until the page is loaded so you see the fonts resize.
Below you can see code for a very simple app that does this. It is easiest to use this in the simulator by setting the initial orientation to landscape by adding this to the info.plist:
UIInterfaceOrientation
UIInterfaceOrientationLandscapeRight
And here is the code:
// webviewAppDelegate.m
#import "webviewAppDelegate.h"
#implementation webviewAppDelegate
#synthesize window;
UIWebView *wv;
-(void) doWebView {
wv = [[UIWebView alloc] init];
wv.frame = CGRectMake(0,0,480,300);
[self.view addSubview:wv];
NSURLRequest *r = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://mobile.washingtonpost.com"]];
wv.delegate = self;
[wv loadRequest:r];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// HACK
wv.frame = CGRectMake(0,0,320,300);
wv.frame = CGRectMake(0,0,480,300);
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
[window makeKeyAndVisible];
UIView *blank = [[UIView alloc] init];
self.view = blank;
[window addSubview:self.view];
NSTimer *t = [NSTimer timerWithTimeInterval : 2.0target: selfselector: #selector(doWebView) userInfo:nil repeats: NO];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
returnYES;
}
- (void)dealloc {
[window release];
[super dealloc];
}
#end
--------------------------------------------------------
// webviewAppDelegate.h
#import <UIKit/UIKit.h>
#interface webviewAppDelegate : UIViewController <UIApplicationDelegate, UIWebViewDelegate> {
UIWindow *window;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end

Related

UIImagePickerControllerCameraDeviceFront works every other time

This question is very similar to an existing question asked here UIImagePickerControllerCameraDeviceFront only works every other time I tried the solution presented but it didn't work for me
I have a simplest of a project with two view controllers. In the blue one I am displaying a small UIView with a UIImagePickerController in it. NOTE: I am displaying front facing camera when app is launched.
I hit the next button and go to orange view controller and when I hit the back button and come back to blue view controller the UIImagePickerController flips from Front to rear. I guess the reason is that it thinks its busy and moves to the rear cam. If I keep moving back and forth between the view controllers the camera keeps flipping front, back, front, back, front, back...
Here is my code and screenshots, what am I doing wrong?
In my *.h
#import <UIKit/UIKit.h>
#interface v1ViewController : UIViewController <UIImagePickerControllerDelegate>
{
UIImagePickerController *picpicker;
UIView *controllerView;
}
#property (nonatomic, retain) UIImagePickerController *picpicker;
#property (nonatomic, retain) UIView *controllerView;
#end
In my *.m file (This code is only used when blue colored view controller is displayed)
#import "v1ViewController.h"
#import <MobileCoreServices/UTCoreTypes.h>
#implementation v1ViewController
#synthesize picpicker;
#synthesize controllerView;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
controllerView = picpicker.view;
[controllerView setFrame:CGRectMake(35, 31, 250, 250)];
controllerView.alpha = 0.0;
controllerView.transform = CGAffineTransformMakeScale(1.0, 1.0);
[self.view addSubview:controllerView];
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
controllerView.alpha = 1.0;
}
completion:nil
];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
You are dismissing the controller in both the viewDidDisappear and viewWillDisappear methods.
That could be the cause of your problem.
Although I do not have a device with a camera available right now to verify this, it seems that you're not dismissing the pickerview controller correctly. The documentation states that you should call dismissModalViewControllerAnimated: on the parent controller in order to dismiss the picker (though, calls to presented controllers will propagate to presenters - so this is not the problem), but in your case you're not displaying the controller modally in the first place so it will not work.
What I would try in this case is to release the picker instead (if not under ARC) and set it to nil (instead of calling [picpicker dismissModalViewControllerAnimated:YES];).
PS. In fact, it seems that there is a bigger problem with your design. Since each button is set to present the other party modally you are not dismissing any of the controllers ever. The controllers just keep stacking on each other. You should either consider to embed them in a navigation controller and have it handle the hierarchy or just set dismissModalViewControllerAnimated: (dismissViewControllerAnimated:completion: on iOS5+) as the action of the second controller's button instead of a modal segue.
This is a very simple issue. I don't know why this happens exactly, but it seems that UIImagePickerController was designed to recreated each time it's needed instead of keeping any reference to it, which seems logical if you think about it. Basically, you need to recreate and reconfigure your picker each time. Below I've pasted some code to give an image of what I mean.
Simple solution:
- (UIImagePickerController *)loadImagePicker {
UIImagePickerController *picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
return picpicker;
}
and in:
-(void)viewWillAppear:(BOOL)animated{
if(!self.picpicker){
self.picpicker = [self loadImagePicker];
[self.view addSubview: self.picpicker];
}
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.picpicker removeFromSuperview];
self.picpicker = nil;
}

How can I make a video display in Landscape mode on iOS 5?

I'm having a very strange problem. I want a video to appear in landscape mode, but I can't seem to make it work. Even if I can't make it always show Landscape, at least I want it to show ok, and I can't make that either!! Here is my code:
#import "SplashViewController.h"
#import "MainViewController.h"
#import "MediaPlayer/MediaPlayer.h"
#interface SplashViewController ()
#property (nonatomic, retain) NSTimer *timer;
#end
#implementation SplashViewController
#synthesize timer = _timer;
-(BOOL)shouldAutorotateToInterfaceOrientation:UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
- (id)init
{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self = [self initWithNibName:#"SplashViewController_iPhone" bundle:nil];
} else {
self = [self initWithNibName:#"SplashViewController_iPad" bundle:nil];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSString *url = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"intro.mp4"];
playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:[playerViewController moviePlayer]];
[playerViewController shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationLandscapeRight];
[self.view addSubview:playerViewController.view];
//play movie
MPMoviePlayerController *player = [playerViewController moviePlayer];
player.scalingMode = MPMovieScalingModeAspectFill;
[player play];
}
- (void) movieFinishedCallback:(NSNotification*) aNotification {
MPMoviePlayerController *player = [aNotification object];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
[player stop];
[player.view removeFromSuperview];
[self loadMainView];
}
- (void)loadMainView
{
MainViewController *mainVC = [[MainViewController alloc] init];
[self.navigationController pushViewController:mainVC animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
And here comes the weirdness...
If I start the app with my iPad physically in Landscape Mode, the video shows like this (please not that the bar at the top is shorter than the widht! :O)
If I then rotate the iPad to Portrait, it looks like this:
But then, if I start the app with my iPad physically in Portrait Mode, the video shows like this:
And if I then rotate the iPad to Landscape, it looks like this:
Which is GREAT! This final image is what I would like the video to always look like.
Any ideas what I might be doing wrong???
Thanks!
EDIT 1
Ok, with #Tark answer I was able to fix the player display issue. Now it's showing fine no matter how I start the app. Thanks for that!! What is missing now is the always landscape mode.
I tried with the following methods:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation==UIInterfaceOrientationLandscapeRight)
return YES;
return NO;
}
I also tried inserting the row
Initial interface orientation = Landscape (right home button)
In the Info.plist
What I'm getting is that if I start the app in Landscape mode, if I rotate the iPad to Portrait, it stays in Landscape. GREAT!
But if I start the app in Portrait mode, the video shows in Portrait mode. Once I rotate it to Landscape, I can't rotate it back to Portrait, which is good, but I don't want it to start in Portrait!
EDIT 2
Ok, now this is even more weird. If I try it on an iPhone, it works great. No matter if I start the app in Landscape or Portrait, the video is shown always in Landscape.
But if I try it on an iPad, the problem in EDIT 1 arises... :S
Any ideas?
Thanks!
Have you tried setting the frame of the MPMoviePlayerViewControllers view when you add it as a subview?
...
playerViewController.view.frame = self.view.bounds;
[self.view addSubview:playerViewController.view];
...
To make the app only run in landscape mode, you should make sure that you have only selected the orientations you want in the app plist. In Xcode 4 there is a handy Supported Interface Orientations section in the target settings, make sure you only select landscape here. If you still have the issue, you have to make sure that you are disabling autorotation on all visible controllers in the view stack.
shouldAutorotateToInterfaceOrientation is deprecated as of iOS 6, Have you tried using supportedInterfaceOrientations?
If you are trying to support iOS 5 & 6 then I believe you need to use both:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
I haven't tested this so take it for what it's worth.

MPMoviePlayerController full screen orientation issue

My app supports only Landscape. I've added an MPMoviePlayerController to the view of my view controller.
When I press full screen button, it works fine and it will rotate in Landscape only for iOS versions prior to iOS 5. However, in iOS 5.0+, it also supports portrait (only when I enter into full screen mode).
How can I prevent portrait support in iOS 5.0 and above?
Try subclassing MPMoviePlayerViewController and overriding the shouldAutorotatoToInterfaceOrientation method to only support landscape modes:
-(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
if((toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight))
{
return true;
}
else
{
return false;
}
}
I resolved this problem thus: create custom navigation controller what support 2 orientation:
UIInterfaceOrientationLandscapeLeft && UIInterfaceOrientationLandscapeRight
More details:
1. Create custom navigation controller
CustomNavigationController.h file
#import <UIKit/UIKit.h>
#interface CustomNavigationController : UINavigationController
-(CustomNavigationController*)initWithRootViewController:(UIViewController *)rootViewController;
#end
CustomNavigationController.m file
#implementation IORNavigationController
-(CustomNavigationController*)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithRootViewController:rootViewController];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
2.In Appdelegate add self navigation controller
Appdelegate.h
#property (nonatomic, retain) CustomNavigationController* navigationController;
Appdelegate.m
self.navigationController = [[[CustomNavigationController alloc] initWithRootViewController:start] autorelease];
self.navigationController.view.autoresizesSubviews = YES;
window.rootViewController = self.navigationController;
[self.navigationController setNavigationBarHidden:YES];
And now you have app with two orientation and video in landscape orientation.

How to detect zoom scale of UIWebView

I want to increase height of UIWebView while user zoom in (like default mail app in iPhone).
So, I have tried the code below to find scrollview in webview and add UIScrollViewDelegate to use scrollViewDidZoom for detecting zoom scale to increase height of webview on this method.
// MessageAppDelegate.h
#interface MessageAppDelegate : NSObject <UIApplicationDelegate,UIWebViewDelegate,UIScrollViewDelegate> {
UIWebView *webview;
UIScrollView *scrollview;
}
//MessageAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
[self.window makeKeyAndVisible];
webview = [[UIWebView alloc] initWithFrame:CGRectMake(0, 150, 320, 100)];
webview.delegate = self;
webview.scalesPageToFit = YES;
webview.userInteractionEnabled = YES;
[webview loadHTMLString:#"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=6.0 user-scalable=yes\">test test" baseURL:nil];
[self.window addSubview:webview];
// Find scrollview in webview
for (UIView *view in webview.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
// Get UIScrollView object
scrollview = (UIScrollView *) view;
scrollview.delegate = self;
}
}
return YES;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale{
NSLog(#"scale %f",scale);
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
NSLog(#"scrollViewDidZoom %f",scrollview.zoomScale);
}
The problem is I cannot zoom in/out on the webview
but NSLog on scrollViewDisEndZooming method showed
scale 1.0
when I ended zooming
and scrollViewDidZoom method didn't show anything.
I want to detect zoom scale of webview for calculating its height on scrollViewDidZoom.
What I've done wrong?
Please help.
Thanks in advance.
I have done well for your code up,do following modificaitons:
you just get the scrollview class point ,then can get the scrollview.contentSize.width,which can change when you zoom in out,but scrollview.zoomScale always remains 1.so have to use contensize.width to calculate scale.
#interface dictViewController : UIViewController<UIWebViewDelegate,UITextFieldDelegate,UIGestureRecognizerDelegate,UIScrollViewDelegate>{
UIScrollView *scrollview;
......................
//scrollview.delegate = self;
for (UIView *view in webViewM.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
// Get UIScrollView object
scrollview = (UIScrollView *) view;
//scrollview.delegate = self;
}
}
/*
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale{
//NSLog(#"scale zooming %f",scale);
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
//NSLog(#"scrollViewDidZoom %f",scrollview.zoomScale);
}*/
- (void)swipeRightAction:(id)ignored
{
NSLog(#"wid=%f", scrollview.contentSize.width);
NSLog(#"zoomscale=%f", scrollview.zoomScale);
}
I don't think there's a proper way to do it from the exposed APIs, but there is a hacky way.
A UIWebView contains a UIScrollView which can be given a delegate. Then when zooming changes, you (the delegate) get a callback called scrollViewDidZoom:.
In order to find the scroll view, you can get the subviews collection from the web view and iterate to see which one isKindOfClass:[UIScrollView class].
Now, UIWebView also conforms to UIScrollViewDelegate. So that may mean you are changing the web view's implementation, and that could be a Bad Idea. Test well.

Handling an external screen on the iPad

Ok, I think its possible I've misunderstood the correct way to implement an external screen on the iPad and it is causing me a lot of headaches.
Since this is a long post, what I'm trying to do is create and send a view to an external screen over VGA, and remove the screen once I'm done with it. I'm having retain count issues so can't get it to work.
I have a view controller that can be called up onto the iPad at any time. When this view loads (it is a remote, similar to Keynote presentation) I check for an external screen, then create a window and add a view to the extra monitor.
in my ipadViewController.h <-- the view that stays on the iPad
I have
#interface ipadViewController : UIViewController {
PresentationViewController *presentationView;
UIScreen *externalScreen;
UIWindow *externalWindow;
}
#property (nonatomic, retain) UIScreen *externalScreen;
#property (nonatomic, retain) UIWindow *externalWindow;
#property (nonatomic, retain) PresentationViewController *presentationView;
#end
(There is more, but that is the external screen stuff).
in ipadViewController.m:
#synthesize externalScreen;
#synthesize externalWindow;
#synthesize presentationView;
So I try to do a few things when the view loads:
Get the external screen (if possible)
Create apresentationViewController and add it to the extra screen
- (void)viewDidLoad {
[super viewDidLoad];
[self getExternalScreen];
[self createPresentationAndSendToWindow];
}
to get the screen I do the following getExternalScreen::
if ([[UIScreen screens] count] > 1)
{
for (UIScreen *currentScreen in [UIScreen screens])
{
if (currentScreen != [UIScreen mainScreen])
self.externalScreen = [currentScreen autorelease];
}
}
and to send the view to it createPresentationAndSendToWindow::
if (self.presentationPath == nil) return;
PresentationViewController *viewController = [[PresentationViewController alloc] initWithNibName:#"CanvasPresentation" bundle:nil];
self.presentationView = viewController;
[viewController release];
if (self.externalWindow == nil)
{
CGRect externalBounds = [self.externalScreen bounds];
self.externalWindow = [[[UIWindow alloc] initWithFrame:externalBounds] autorelease];
[self.externalWindow addSubview:self.presentationView.view];
self.externalWindow.screen = self.externalScreen;
[self.externalWindow makeKeyAndVisible];
}
in dealloc I try to cleanup with:
[presentationView release];
[externalScreen release];
//[externalWindow release]; <- that would crash
Problem I have is that when I dismiss the remoteViewController (it is modal), after releasing externalScreen has a retain count = 1 and externalWindow has retain count = 2.
The crash caused by externalWindow release disappears if I don't release presentationView (but then I'm leaking presentationView.
Your problem is here:
for (UIScreen *currentScreen in [UIScreen screens])
{
if (currentScreen != [UIScreen mainScreen])
self.externalScreen = [currentScreen autorelease];
}
Remove the autorelease. You shouldn't be releasing something you didn't create or retain.
you are autoreleasing externalView on
self.externalWindow = [[[UIWindow alloc] initWithFrame:externalBounds] autorelease];
and then you assign an autorelease view to it
self.externalScreen = [currentScreen autorelease];
you cannot release an autoreleased view, or it will crash.