For my IOS hybrid app I create a WKWebView programatically as shown below
- (void)viewDidLoad
{
[super viewDidLoad];
_statBarH = [UIApplication sharedApplication].statusBarFrame.size.height ;
appH = self.view.frame.size.height ;
appW = self.view.frame.size.width ;
webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:theConfiguration];
smallScreen = [deviceType hasPrefix:#"iPhone"];
if (smallScreen && appW > appH) {webView.frame = CGRectMake(0, 0 , appW, appH);}
else {webView.frame = CGRectMake(0, _statBarH, appW, appH - _statBarH);};
};
I also change the size of the web view when the device rotates as follows:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
appH = size.height ;
appW = size.width ;
if (smallScreen && appW > appH) {webView.frame = CGRectMake(0, 0 , appW, appH);}
else {webView.frame = CGRectMake(0, _statBarH, appW, appH - _statBarH);};
}
completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
[webView evaluateJavaScript:#"IOSOrientationChanged()" completionHandler:nil];
}
];
}
The javascript function IOSOrientationChanged merely redraws my app content in either portrait or landscape format, detecting the new height and width from javascript window.innerWidth and window.innerHeight
This approach has worked fine for me, until Apple removed the home button on iPhone X, 11 ...
What happens on these newer phones (at least in the XCode 11 IOS 13 simulator as I cannot afford one for real) is that when the web view is first created, it respects the upper safe area, but does not respect the lower safe area.
However when I rotate the device in the simulator, the webview respects both the safe areas.
For iPhone 11, the variables appW and appH are 414 and 896 in portrait and 896 and 414 in landscape. The variable _statBarH is 44 in portrait and 0 in landscape.
However inside the web view, window.innerHeight in portrait is initially set to 852 (896-44), but after rotating it is set to 838.
Please can someone tell me what I am doing wrong? Are some internal view constraints not being enforced the first time, but are being enforced after rotation?
The solution to this, is two fold
Firstly create the WKWebView in viewDidAppear, rather than in viewDidLoad, as it is not until viewDidAppear that you can programatically access the safeAreaInserts values
Secondly, use the values in safeAreaInserts when creating the WKWebView frame, as below
smallScreen = [deviceType hasPrefix:#"iPhone"];
topMargin = [UIApplication sharedApplication].statusBarFrame.size.height ;
if (#available(iOS 11, *)) topMargin = self.view.safeAreaInsets.top ;
bottomMargin = 0 ;
if (#available(iOS 11, *)) bottomMargin = self.view.safeAreaInsets.bottom ;
int top = (smallScreen && appW > appH)? 0 : topMargin ;
int height = (smallScreen && appW > appH)? appH : appH - topMargin - bottomMargin ;
NSLog (#"Top %d, Height %d",top,height);
webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, top , appW, height) configuration:theConfiguration];
Related
I have a UIScrollView which scrolls horizontally from left to right.
I want to be able to vertically position my UIScrollView on the Y axis according to the screen size of the iPhone. E.g iPhone 4 and iPhone 5.
CGFloat startX = (70.0f * ((float)[_attachments count] - 1.0f) + padding);
CGFloat startY = 295;
CGFloat width = 64;
CGFloat height = 64;
Right now I start my Y position at 295 which works okay with the iPhone 4 but not on iPhone 5.
How would I change the startY to accommodate the same position for different screen size?
CGRect screenBounds = [[UIScreen mainScreen] bounds];
if (screenBounds.size.height == 568)
{
//iphone5
}
else
{
//iphone4
}
You can set your y value accordingly
Do use layoutSubviews in your view ...
- (void)layoutSubviews {
CGRect frame = self.bounds;
frame.origin.y += frame.size.height - _scrollView.frame.size.height;
if ( ! CGRectEqualToRect( _scrollView.frame, frame ) {
_scrollView.frame = frame;
}
}
... I assume that your horizontal scroll view is in _scrollView ivar and also that the _scrollView.frame.size.height contains correct height of your _scrollView. If not, replace _scrollView.frame.size.height with some constant which does contain your UIScrollView height.
Why the condition with CGRectEqualToRect? Because layoutSubviews can be called very often and when you set new frame (even if it equals) to it, UIScrollView can stop scrolling.
you can do it like this-
first get the width and the height of the screen
CGRect screenBounds = [UIScreen mainScreen].bounds ;
CGFloat width = CGRectGetWidth(screenBounds);
CGFloat height = CGRectGetHeight(screenBounds) ;
now create a method which will do some calculations and return an int
-(NSInteger) getwidth:-(NSInteger *) value
{
int temp = (value * 100) / 480;
return int((height * temp) / 100);
}
-(NSInteger) getheight:-(NSInteger *) value
{
var temp = (value * 100) / 320;
return int((width * temp) / 100);
}
now set the bounds of view according to these function
actind.frame=CGRectMake(getWidth(20),getheight(20),16,16);
this will work on all the devices and the views will arrange themselves according to the width and height of the screen.
hope this will help for you. :)
The Problem
I have a UIWebView inside my iPad application which I need to zoom programmatically, but without the use of gestures/taps. The app. has a "+" and a "-" button for zooming in-and-out in user-defined increments (it is an app. for the visually impaired).
This +/- zoom-button functionality previously worked 100% fine when my app. used a UIImageView inside of a UIScrollView (instead of the WebView). The ImageView was the actual view which was zoomed, and the CenterPoint was calculated for the ScrollView's zoomToRect method.
I now have a WebView, which I know contains a ScrollView as a subview. I tried adapting the code which previously worked with the ImageView/ScrollView combo to instead zoom the WebView's ScrollView, but it is no longer calculating the CenterPoint correctly for zoomToRect:.
What Happens:
The WebView zooms correctly in-terms of the zoom-level, but the center point is always wrong. For some reason, the screen always zooms-in on the top-left every time.
Another odd problem, is that after the very first time you zoom-in, you cannot scroll in the WebView past the visible portion of the screen. If you try to, it shows a bit of the content past the bounds of the currently visible area, but it instantly snaps-back.
What I have tried:
I am trying to zoom the UIWebView's ScrollView.
I create a pointer to the ScrollView, and set "self" as its delegate. I then setup various variables, such as scrSize (the size of the view to zoom) and ZoomHandler (explained below):
- (void)viewDidLoad {
// ... Various UIWebView setup ...
[self LoadURL];
// Zooming & scrollview setup
zoomHandler = [[ZoomHandler alloc] initWithZoomLevel: ZOOM_STEP];
scrSize = CGPointMake(self.WebView.frame.size.width, self.WebView.frame.size.height);
scrollView = [WebView.subviews objectAtIndex:0];
[scrollView setTag:ZOOM_VIEW_TAG];
[scrollView setMinimumZoomScale:MINIMUM_SCALE];
[scrollView setZoomScale:1];
[scrollView setMaximumZoomScale:10];
scrollView.bounces = FALSE;
scrollView.bouncesZoom = FALSE;
scrollView.clipsToBounds = NO;
[scrollView setDelegate:self];
[super viewDidLoad];
}
To override the WebView's default zooming limitations, I inject this Javascript into the loaded webpage in the webViewDidFinishLoad: method:
function increaseMaxZoomFactor() {
var element = document.createElement('meta');
element.name = "viewport";
element.content = "maximum-scale=10 minimum-scale=1 initial-scale=1 user-scalable=yes width=device-width height=device-height;"
var head = document.getElementsByTagName('head')[0];
head.appendChild(element);
}
CenterPoint Code:
This code is used to calculate the CenterPoint to pass into zoomToRect:. This worked 100% fine when I was zooming an ImageView inside of a ScrollView.
-(IBAction)zoomOut {
float newScale = [scrollView zoomScale] / ZOOM_STEP;
if( [scrollView zoomScale] > MINIMUM_SCALE) {
[self handleZoomWith:newScale andZoomType: FALSE];
}
}
-(IBAction)zoomIn {
float newScale = [scrollView zoomScale] * ZOOM_STEP;
if( [scrollView zoomScale] < MAXIMUM_SCALE){
[self handleZoomWith:newScale andZoomType: TRUE];
}
}
-(void)handleZoomWith: (float) newScale andZoomType:(BOOL) isZoomIn {
CGPoint newOrigin = [zoomHandler getNewOriginFromViewLocation: [scrollView contentOffset]
viewSize: scrSize andZoomType: isZoomIn];
CGRect zoomRect = [self zoomRectForScale:newScale withCenter:newOrigin];
[scrollView zoomToRect:zoomRect animated:YES];
}
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {
CGRect zoomRect;
// At a zoom scale of 1.0, it would be the size of the scrollView's bounds.
// As the zoom scale decreases, so more content is visible, the size of the rect grows.
zoomRect.size.height = [WebView frame].size.height / scale;
zoomRect.size.width = [WebView frame].size.width / scale;
// Choose an origin so as to get the right center.
zoomRect.origin.x = center.x / scale;
zoomRect.origin.y = center.y / scale;
return zoomRect;
}
/**
Determine the origin [THIS IS INSIDE ZOOMHANDLER]
*/
-(CGPoint) getNewOriginFromViewLocation: (CGPoint) oldOrigin viewSize: (CGPoint) viewSize andZoomType:(BOOL) isZoomIn {
// Calculate original center (add the half of the width/height of the screen)
float oldCenterX = oldOrigin.x + (viewSize.x / 2);
float oldCenterY = oldOrigin.y + (viewSize.y / 2);
// Xalculate the new center
CGPoint newCenter;
if(isZoomIn) {
newCenter = CGPointMake(oldCenterX * zoomLevel, oldCenterY * zoomLevel);
} else {
newCenter = CGPointMake(oldCenterX / zoomLevel, oldCenterY / zoomLevel);
}
// Calculate the new origin (deduct the half of the width/height of the screen)
float newOriginX = newCenter.x - (viewSize.x / 2);
float newOriginY = newCenter.y - (viewSize.y / 2);
return CGPointMake(newOriginX, newOriginY);
}
Does anyone have any idea why the CenterPoint is not being calculated correctly? Any help would be GREATLY appreciated; I have been stuck on this for a week now.
Thanks,
Alex
Have you tried implementing the zoom using JavaScript rather than the UIWebView's scrollview ?
I believe you can zoom the webview by calling:
[webView stringByEvaluatingJavaScriptFromString:#"document.body.style.zoom = 5.0;"]; (use your ZOOM_STEP as the value)
You could also calculate the center of the browser window using JS:
posY = getScreenCenterY();
posX = getScreenCenterX();
function getScreenCenterY() {
var y = 0;
y = getScrollOffset()+(getInnerHeight()/2);
return(y);
}
function getScreenCenterX() {
return(document.body.clientWidth/2);
}
function getInnerHeight() {
var y;
if (self.innerHeight) // all except Explorer
{
y = self.innerHeight;
}
else if (document.documentElement && document.documentElement.clientHeight)
// Explorer 6 Strict Mode
{
y = document.documentElement.clientHeight;
}
else if (document.body) // other Explorers
{
y = document.body.clientHeight;
}
return(y);
}
function getScrollOffset() {
var y;
if (self.pageYOffset) // all except Explorer
{
y = self.pageYOffset;
}
else if (document.documentElement &&
document.documentElement.scrollTop) // Explorer 6 Strict
{
y = document.documentElement.scrollTop;
}
else if (document.body) // all other Explorers
{
y = document.body.scrollTop;
}
return(y);
}
(source: http://sliceofcake.wordpress.com/2007/09/13/use-javascript-to-find-the-center-of-the-browser/)
for (UIScrollView *scroll in [myPDFView subviews]) {
//Set the zoom level.
[scroll setZoomScale:2.5f animated:YES];
}
I am making a view with several subviews for iPad. I want all the positioning and sizing of the subviews to stay the same when rotating the iPad, but the content inside the subviews to re-orient. Does anyone know how to do this?
It sounds like you still need to layout the views depending on the orientation. However, you're portrait and landscape frames would have different proportions on the screen.
An Example of how you might layout a view:
int h = 300;
int w = 100;
int space = 50;
CGRect frame;
if( interfaceOrientation == UIInterfaceOrientationPortrait ) {
frame = CGRectMake( space, space, w, h );
}
else if( interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ) {
frame = CGRectMake( self.view.bounds.size.width - w - space, self.view.bounds.size.height- h - space, w, h );
}
else if( interfaceOrientation == UIInterfaceOrientationLandscapeRight ) {
frame = CGRectMake( space, self.view.bounds.size.height- w - space, h , w );
}
else if( interfaceOrientation == UIInterfaceOrientationLandscapeLeft ) {
frame = CGRectMake( self.view.bounds.size.width - h - space, space, h, w );
}
self.label.frame = frame;
I'm trying to embed OpenFlow in my iPhone app, but I can't figure out how I can resize it in a landscape view with a navigation bar and a toolbar. The covers are too big and the effect is really ugly. Can anyone help me?
Just add a multiplier to the values of the frame at the setImage function on AFItemView.m
- (void)setImage:(UIImage *)newImage originalImageHeight:(CGFloat)imageHeight reflectionFraction:(CGFloat)reflectionFraction {
float multiplier = 1.5;
[imageView setImage:newImage];
verticalPosition = imageHeight * reflectionFraction / 2;
originalImageHeight = imageHeight;
self.frame = CGRectMake(0, 0, newImage.size.width*multiplier, newImage.size.height*multiplier);
}
I have a small UIView that should appear always in the center of the screen. This works great for portrait mode but does not in landscape.
To draw the UIView I use:
myView.frame = CGRectMake((visibleArea.size.width - MY_VIEW_WIDTH) / 2, (visibleArea.size.height - MY_VIEW_HEIGHT) / 2, MY_VIEW_WIDTH, MY_VIEW_HEIGHT);
myView.autoresizingMask = (
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin |
UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin
);
Any ideas what might be missing?
Why not try
myView.center = controller.view.center
Is the app auto rotating?
I mean, what does your -[UIViewController shouldAutorotateToInterfaceOrientation:] method looks like? Do you have an initial and supported orientations in your Info.plist file?
Check also parent's view autoresizesSubviews property.
Maybe you don't change the orientation of the status bar. If you don't do this, the device thinks that the orientation still portrait, not landscape.
I created a property to give me the bounds, based on the orientation of the device. (This is in a category on UIScreen.)
- (CGRect)boundsWithRespectToOrientation
{
CGRect bounds = self.bounds;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && bounds.size.height > bounds.size.width)
bounds = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.height, bounds.size.width);
else if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) && bounds.size.width > bounds.size.height)
bounds = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.height, bounds.size.width);
return bounds;
}