detect when uiscrollview frame touch the main screen bounds - iphone

how can i detect when a UIScrollViewframe intersect with the main screen (when scrolling) in both direction up and down? i was trying to do it like so :
- (void)detectScreenBoundsIntersect{
if (CGRectIntersectsRect([myScrollView frame], [[UIScreen mainScreen] bounds])) {
NSLog(#"COLLISION");
}
}
But this doesn't seem to work! Thanks.

Adopt the UIScrollViewDelegate protocol in the view controller and set the scroll view's delegate to the controller. Adopt the scrollViewDidScroll: method.
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
if ( [self hasReachedAVerticalEdge] ) {
NSLog(#"At World's End");
}
}
- (BOOL) hasReachedAVerticalEdge {
CGPoint offset = myScrollView.contentOffSet;
CGSize contentSize = myScrollView.contentSize;
CGFloat height = myScrollView.frame.size.height;
CGFloat width = myScrollView.frame.size.width;
if ( offset.y == 0 ||
(offset.y + height) == contentSize.height ) {
return YES;
}
return NO;
}
Is this what you are looking for?
Checking if an image view falls in the visible portion of the scroll view.
- (BOOL)isContentFrameVisible:(CGRect)aFrame {
CGRect visibleRect = CGRectZero;
visibleRect.origin = myScrollView.offset;
visibleRect.size = myScrollView.frame.size;
if ( CGRectIntersectsRect(visibleRect, aFrame) ) {
return YES;
}
}

Related

UIScrollview with paging like effect

I have a uiscrollview. Scrollview contains multiple uiview as subview.I want paging like animation effect to scrollview. I have set scrollview.pagingEnabled=NO.
Following is my code
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
lastContentOffset = scrollView.contentOffset.x;
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
ScrollDirection scrollDirection;
if (self.lastContentOffset >scrollView.contentOffset.x) {
scrollDirection = ScrollDirectionRight; } else if (self.lastContentOffset < scrollView.contentOffset.x) {
scrollDirection = ScrollDirectionLeft;
}
if (scrollDirection==ScrollDirectionRight) {
CGFloat xOffset = scrollview.contentOffset.x;
CGFloat yOffset = scrollview.contentOffset.y;
if (scrollview.contentOffset.x != scrollview.frame.origin.x)
{
[scrollview setContentOffset:CGPointMake(xOffset- 320, yOffset) animated:YES];
}
NSLog(#" ScrollDirectionRight custom x==%f %f", scrollview.contentOffset.x, scrollview.contentSize.width);
} else if(scrollDirection==ScrollDirectionLeft) {
CGFloat xOffset = scrollview.contentOffset.x;
CGFloat yOffset = scrollview.contentOffset.y;
if ((scrollview.contentOffset.x != scrollview.frame.origin.x) )
{
[scrollview setContentOffset:CGPointMake(xOffset + 320, yOffset) animated:YES];
}
NSLog(#"ScrollDirectionLeft custom x==%f %f", scrollview.contentOffset.x, scrollview.contentSize.width);
}
}
Through this code im getting paging like effect.but the effect is not smooth.
Thanks
Check out UIScrollViewDelegate method
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
In this callback you will modify 'targetContentOffset' to where you like to see scroll offset animation end.
Using it you can make custom pagination implementation a lot smoother.

imageview in a scrollview not zooming in some portion of view

i've implemented imageview zooming in scrollview application in my app.
i want to set the scrollview and imageview on the right top of the view. but it's not zooming there. when i placed it on centre of view it's zooming.
The code is given below
#interface przoomViewController : UIViewController<UIScrollViewDelegate>
{
IBOutlet UIScrollView *scrollView;
IBOutlet UIImageView *image;
}
-(void)viewDidLoad
{
[image setUserInteractionEnabled:YES];
[scrollView addSubview:image];
scrollView.scrollEnabled = YES;
scrollView.minimumZoomScale=1.0;
scrollView.maximumZoomScale=4.0;
[scrollView setContentSize:CGSizeMake(320, 180)];
scrollView.delegate=self;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return image;
}
please help me.
I have implemented image zooming in my app. Try scrollviewDelegate Method
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
// The scroll view has zoomed, so we need to re-center the contents
[self centerScrollViewContents];
}
- (void)centerScrollViewContents
{
CGSize boundsSize = zoomingScrollView.bounds.size;
CGRect contentsFrame = ZImageView.frame;
if (contentsFrame.size.width < boundsSize.width)
{
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
}
else
{
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height)
{
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
}
else
{
contentsFrame.origin.y = 0.0f;
}
ZImageView.frame = contentsFrame;
}
No need to set userInterAction enabled on imageView. If this does not help, I will share my implementation.
You should set value for zoomScale property of scrollview to start zooming as given below.
scrollView.zoomScale=1.0; // this will enable zooming.

UITableView with UIRefreshControl under a semi-transparent status bar

I've created a UITableView which I want to scroll underneath my semi-transparent black status bar. In my XIB, I just set the table view's y position to -20 and it all looks fine.
Now, I've just added a pull-to-refresh iOS6 UIRefreshControl which works however, because of the -20 y position, it drags from behind the status bar. I'd like it's "stretched to" position to be under the status bar rather than behind.
It makes sense why it's messing up but there doesn't seem to be any difference changing it's frame and the tableview's content insets etc don't make a difference.
The docs suggest that once the refreshControl has been set, the UITableViewController takes care of it's position from then on.
Any ideas?
You can subclass the UIRefreshControl and implement layoutSubviews like so:
#implementation RefreshControl {
CGFloat topContentInset;
BOOL topContentInsetSaved;
}
- (void)layoutSubviews {
[super layoutSubviews];
// getting containing scrollView
UIScrollView *scrollView = (UIScrollView *)self.superview;
// saving present top contentInset, because it can be changed by refresh control
if (!topContentInsetSaved) {
topContentInset = scrollView.contentInset.top;
topContentInsetSaved = YES;
}
// saving own frame, that will be modified
CGRect newFrame = self.frame;
// if refresh control is fully or partially behind UINavigationBar
if (scrollView.contentOffset.y + topContentInset > -newFrame.size.height) {
// moving it with the rest of the content
newFrame.origin.y = -newFrame.size.height;
// if refresh control fully appeared
} else {
// keeping it at the same place
newFrame.origin.y = scrollView.contentOffset.y + topContentInset;
}
// applying new frame to the refresh control
self.frame = newFrame;
}
It takes tableView's contentInset into account, but you can change topContentInset variable to whatever value you need and it will handle the rest.
I hope the code is documented enough to understand how it works.
Just subclass the UIRefreshControl and override layoutSubviews like this:
- (void)layoutSubviews
{
UIScrollView* parentScrollView = (UIScrollView*)[self superview];
CGSize viewSize = parentScrollView.frame.size;
if (parentScrollView.contentInset.top + parentScrollView.contentOffset.y == 0 && !self.refreshing) {
self.hidden = YES;
} else {
self.hidden = NO;
}
CGFloat y = parentScrollView.contentOffset.y + parentScrollView.scrollIndicatorInsets.top + 20;
self.frame = CGRectMake(0, y, viewSize.width, viewSize.height);
[super layoutSubviews];
}
The current upvoted answer does not play well with the fact you pull the component down (as Anthony Dmitriyev pointed out), the offset is incorrect. The last part is to fix it.
Either way: subclass the UIRefreshControl with the following method:
- (void)layoutSubviews
{
UIScrollView* parentScrollView = (UIScrollView*)[self superview];
CGFloat extraOffset = parentScrollView.contentInset.top;
CGSize viewSize = parentScrollView.frame.size;
if (parentScrollView.contentInset.top + parentScrollView.contentOffset.y == 0 && !self.refreshing) {
self.hidden = YES;
} else {
self.hidden = NO;
}
CGFloat y = parentScrollView.contentOffset.y + parentScrollView.scrollIndicatorInsets.top + extraOffset;
if(y > -60 && !self.isRefreshing){
y = -60;
}else if(self.isRefreshing && y <30)
{
y = y-60;
}
else if(self.isRefreshing && y >=30)
{
y = (y-30) -y;
}
self.frame = CGRectMake(0, y, viewSize.width, viewSize.height);
[super layoutSubviews];
}
UIRefreshControl always sits above the content in your UITableView. If you need to alter where the refreshControl is place, try altering the tableView's top contentInset. The UIRefreshControl takes that into account when determining where it should be positioned.
Try this:
CGFloat offset = 44;
for (UIView *subview in [self subviews]) {
if ([subview isKindOfClass:NSClassFromString(#"_UIRefreshControlDefaultContentView")]) {
NSLog(#"Setting offset!");
[subview setFrame:CGRectMake(subview.frame.origin.x, subview.frame.origin.y + offset, subview.frame.size.width, subview.frame.size.height)];
}
}
This will move UIRefreshControll down for 44 points;
I found the overriding of the layoutsubviews in the other answers did more than change the frame, they also change the behaviour (the control started sliding down with the content). To change the frame but not the behaviour I did this:
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frame = self.frame;
CGFloat desiredYOffset = 50.0f;
self.frame = CGRectMake(frame.origin.x, frame.origin.y + desiredYOffset, frame.size.width, frame.size.height);
}
This works super-smoothly, but it's a super hacky solution:
class CustomUIRefreshControl: UIRefreshControl {
override func layoutSubviews() {
let offset = UIApplication.shared.windows[0].safeAreaInsets.top
frame = CGRect(
x: frame.origin.x,
y: frame.origin.y + offset/2.5,
width: frame.size.width,
height: frame.size.height
)
let attribute = [
NSAttributedString.Key.font: UIFont(name: "Chalkduster", size: offset/2)!,
NSAttributedString.Key.foregroundColor: UIColor.clear
]
attributedTitle = NSAttributedString(string: "Hack", attributes: attribute)
super.layoutSubviews()
}
}

Scaling UIImageView inside UIScrollView with maintaining the rotation

I have a problem in scaling the uiimageview which is placed inside the uiscrollview. I have googled and checked all the questions related to my problem in StackOverflow as well. I tried all the answers that are posted in the StackOverflow also. Nothing worked for me.
First I am placing the uiimageview inside uiscrollview in nib file and I am taking the image from Camera roll and filling the image view. Then I am using uirotationgesturerecognizer to rotate the image.
Here is the code that I am trying to do.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#",[[UIDevice currentDevice] model]);
// Do any additional setup after loading the view, typically from a nib.
self.imagePicker = [[[UIImagePickerController alloc] init] autorelease];
self.picChosenImageView.layer.shouldRasterize = YES;
self.picChosenImageView.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.picChosenImageView.layer.contents = (id)[UIImage imageNamed:#"test"].CGImage;
self.picChosenImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.picChosenImageView.layer.shadowOpacity = 0.8f;
self.picChosenImageView.layer.shadowRadius = 8;
self.picChosenImageView.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.picChosenImageView.bounds].CGPath;
UIRotationGestureRecognizer *rotationRecognizer = [[[UIRotationGestureRecognizer alloc]initWithTarget:self
action:#selector(handleRotate:)] autorelease];
rotationRecognizer.delegate = self;
[self.picChosenImageView addGestureRecognizer:rotationRecognizer];
self.containerView.delegate = self;
self.containerView.contentSize = self.picChosenImageView.layer.frame.size;
self.containerView.maximumZoomScale = 4.0f;
self.containerView.minimumZoomScale = 1.0f;
angle = 0.0f;
useRotation = 0.0;
isRotationStarted=FALSE;
isZoomingStarted = FALSE;
}
-(void)lockZoom
{
maximumZoomScale = self.containerView.maximumZoomScale;
minimumZoomScale = self.containerView.minimumZoomScale;
self.containerView.maximumZoomScale = 1.0;
self.containerView.minimumZoomScale = 1.0;
self.containerView.clipsToBounds = false;
self.containerView.scrollEnabled = false;
}
-(void)unlockZoom
{
self.containerView.maximumZoomScale = maximumZoomScale;
self.containerView.minimumZoomScale = minimumZoomScale;
self.containerView.clipsToBounds = true;
self.containerView.scrollEnabled = true;
}
#pragma mark - ScrollView delegate methods
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.picChosenImageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
CGRect frame = self.picChosenImageView.frame;
frame.origin = CGPointZero;
self.picChosenImageView.frame = frame;
//self.picChosenImageView.transform = prevTransform;
}
-(void) scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
if(!isZoomingStarted)
{
self.picChosenImageView.transform = CGAffineTransformRotate(self.picChosenImageView.transform, angle);
NSLog(#"The zooming started");
isZoomingStarted = TRUE;
CGSize contentSize = self.containerView.bounds.size;
CGRect contentFrame = self.containerView.bounds;
NSLog(#"frame on start: %#", NSStringFromCGRect(contentFrame));
NSLog(#"size on start: %#", NSStringFromCGSize(contentSize));
//prevTransform = self.picChosenImageView.transform;
}
}
-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
if(isZoomingStarted)
{
self.picChosenImageView.transform = CGAffineTransformRotate(self.picChosenImageView.transform, angle);
isZoomingStarted = FALSE;
CGSize contentSize = self.containerView.contentSize;
CGRect contentFrame = self.containerView.bounds;
NSLog(#"frame on end: %#", NSStringFromCGRect(contentFrame));
NSLog(#"size on end: %#", NSStringFromCGSize(contentSize));
}
}
#pragma mark - GestureRecognizer methods
- (void) handleRotate:(UIRotationGestureRecognizer *)recognizer
{
if(isZoomingStarted == FALSE)
{
if([recognizer state] == UIGestureRecognizerStateBegan)
{
angle = 0.0f;
[self lockZoom];
}
useRotation+= recognizer.rotation;
while( useRotation < -M_PI )
{
useRotation += M_PI*2;
}
while( useRotation > M_PI )
{
useRotation -= M_PI*2;
}
NSLog(#"The rotated value is %f",RADIANS_TO_DEGREES(useRotation));
self.picChosenImageView.transform = CGAffineTransformRotate([self.picChosenImageView transform],
recognizer.rotation);
[recognizer setRotation:0];
if([recognizer state] == UIGestureRecognizerStateEnded)
{
angle = useRotation;
useRotation = 0.0f;
isRotationStarted = FALSE;
self.containerView.hidden = NO;
//prevTransform = self.picChosenImageView.transform;
[self unlockZoom];
}
}
}
My problem is, I am able to successfully do a zoom in and zoom out. I am able to rotate the uiimageview as I wanted to. After rotating the uiimageview to a certain angle, and when I am trying to zoom in, the imageview gets back to the original position (rotate itself back to zero degree) and then the zooming happens. I want to retain the rotation and also zoom. I tried saving the previous transform and assign in back scrollDidzoom and scrollDidBegin delegate methods. None worked. Please help me to spot my mistake which I am overlooking.
try using CGAffineTransformScale instead of just resizing the frame for zooming:
anImage.transform = CGAffineTransformScale(anImage.transform, 2.0, 2.0);
changing the transform for scaling might fix your rotation issue.
hope this helps.
I had the same problem. UIScrollView is taking control over UIImageView and it is using transform without rotation.
So I do not give image reference to scroll and I have added UIPinchGestureRecognizer for scaling.
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return nil
}
Dragging is still working :)
// viewDidLoad
var pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinchRecogniezed))
scrollView.addGestureRecognizer(pinchGestureRecognizer)
func pinchRecogniezed(sender: UIPinchGestureRecognizer) {
if sender.state == .Began || sender.state == .Changed {
let scale = sender.scale
imageView.transform = CGAffineTransformScale(imageView.transform, scale, scale)
sender.scale = 1
}
}

Automatically Sizing UIView after Adding to Window

Note: This may be a duplicate of Subview Doesnt AutoSize When Added to Root View Controller
I have an iPad app that switches between different views in its main window. The view-switching code looks like this:
- (void)switchToViewController:(UIViewController*)viewController {
if (currentViewController != viewController) {
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
[window addSubview:viewController.view];
}
}
The problem is that when the new view (a UISplitView) appears in landscape orientation, it is not sized to fill the entire window. There is a large empty black space on the right. It looks like the view is only 768 pixels wide, rather than the 1024-pixel width of the landscape window.
If I rotate the device to portrait and then back to landscape, the view sizes itself properly.
If the device is in portrait orientation, everything works fine. The UISplitView also gets sized properly if it is the first view I show. The problem only occurs if I switch to it after another view has been shown, in landscape.
So, is there some way to force iPhone OS to resize the view after it has been added to the window?
I've tried calling sizeToFit, and setNeedsLayout. I've also tried setting the view's bounds to the window's bounds, and I've tried setting the frame to match the previous view's frame.
This is absolutely possible! :-)
You can check out my repo here:
https://github.com/hfossli/AGWindowView
It will automatically deal with any rotation and framechanges so you won't have to worry about that.
If you like to worry about that then you can just cut and paste the most important parts
# 1 Add view to window
[[UIApplication sharedApplication] keyWindow] addSubview:aView];
# 2 Add listener and update view
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarFrameOrOrientationChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
Remember to remove notification listening
[[NSNotificationCenter defaultCenter] removeObserver:self];
# 3 Do the math
- (void)statusBarFrameOrOrientationChanged:(NSNotification *)notification
{
/*
This notification is most likely triggered inside an animation block,
therefore no animation is needed to perform this nice transition.
*/
[self rotateAccordingToStatusBarOrientationAndSupportedOrientations];
}
- (void)rotateAccordingToStatusBarOrientationAndSupportedOrientations
{
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
CGFloat angle = UIInterfaceOrientationAngleOfOrientation(statusBarOrientation);
CGFloat statusBarHeight = [[self class] getStatusBarHeight];
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
CGRect frame = [[self class] rectInWindowBounds:self.window.bounds statusBarOrientation:statusBarOrientation statusBarHeight:statusBarHeight];
[self setIfNotEqualTransform:transform frame:frame];
}
- (void)setIfNotEqualTransform:(CGAffineTransform)transform frame:(CGRect)frame
{
if(!CGAffineTransformEqualToTransform(self.transform, transform))
{
self.transform = transform;
}
if(!CGRectEqualToRect(self.frame, frame))
{
self.frame = frame;
}
}
+ (CGFloat)getStatusBarHeight
{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(UIInterfaceOrientationIsLandscape(orientation))
{
return [UIApplication sharedApplication].statusBarFrame.size.width;
}
else
{
return [UIApplication sharedApplication].statusBarFrame.size.height;
}
}
+ (CGRect)rectInWindowBounds:(CGRect)windowBounds statusBarOrientation:(UIInterfaceOrientation)statusBarOrientation statusBarHeight:(CGFloat)statusBarHeight
{
CGRect frame = windowBounds;
frame.origin.x += statusBarOrientation == UIInterfaceOrientationLandscapeLeft ? statusBarHeight : 0;
frame.origin.y += statusBarOrientation == UIInterfaceOrientationPortrait ? statusBarHeight : 0;
frame.size.width -= UIInterfaceOrientationIsLandscape(statusBarOrientation) ? statusBarHeight : 0;
frame.size.height -= UIInterfaceOrientationIsPortrait(statusBarOrientation) ? statusBarHeight : 0;
return frame;
}
CGFloat UIInterfaceOrientationAngleOfOrientation(UIInterfaceOrientation orientation)
{
CGFloat angle;
switch (orientation)
{
case UIInterfaceOrientationPortraitUpsideDown:
angle = M_PI;
break;
case UIInterfaceOrientationLandscapeLeft:
angle = -M_PI_2;
break;
case UIInterfaceOrientationLandscapeRight:
angle = M_PI_2;
break;
default:
angle = 0.0;
break;
}
return angle;
}
UIInterfaceOrientationMask UIInterfaceOrientationMaskFromOrientation(UIInterfaceOrientation orientation)
{
return 1 << orientation;
}
Good luck!
This works, but it seems a little hacky:
- (void)switchToViewController:(UIViewController *)viewController {
if (viewController != currentViewController) {
UIInterfaceOrientation orientation = currentViewController.interfaceOrientation;
[currentViewController.view removeFromSuperview];
currentViewController = viewController;
UIView *view = viewController.view;
// Set appropriate view frame (it won't be autosized by addSubview:)
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
if (UIInterfaceOrientationIsLandscape(orientation)) {
// Need to flip the X-Y coordinates for landscape
view.frame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width);
}
else {
view.frame = appFrame;
}
[window addSubview:view];
}
}
The window may include other UI elements besides your view. The 20 pixel difference in your example is the height of the status bar.
[[UIApplication sharedApplication] statusBarFrame].height;
Neither the window nor screen rotate. Getting their frames and using them for a rotated view will only work if you have switched the height and width.
If you are using a UIViewController, try returning YES from this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation; // Override to allow rotation. Default returns YES only for UIDeviceOrientationPortrait
I got the same problem, but i fixed it with this lines of code:
- (void)changeRow:(NSNotification *)notification {
[window addSubview:new.view];
[old.view removeFromSuperview];
[new.view removeFromSuperview];
[window addSubview:new.view];
}
You must add the new view, then remove the old and the new and then add the new view. I don't know why, but that works.
Fossli's answer is correct for iPad. However, I have a universal app that I needed to support. Therefore some adjustments are necessary.
Add the following to AppDelegate.h
#property (strong, nonatomic) UIImageView *imageView;
Add the following to AppDelegate.m
#synthesize imageView;
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (! (UIInterfaceOrientationIsLandscape(deviceOrientation) ||
UIInterfaceOrientationIsPortrait(deviceOrientation)))
{
// May be "UIInterfaceOrientationUnknown" which does not appear to be a defined value anywhere.
return;
}
[imageView setImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
/*
iOS Image Sizes
iPhone/iPod Portrait 320 x 480 (640 x 960 #2x)
iPad Portrait 768 x 1004 (1536 x 2008 #2x)
Landscape 1024 x 748 (2048 x 1496 #2x)
iPad window bounds in both orientations 768 x 1024 (needs manual swap in landscape)
iPhone window bounds in both orientations 320 x 480 (needs manual swap in landscape)
Note the size variations between the required default launch image sizes and
the size of the window bounds.
iPhone/iPod only requires rotations.
iPad needs origin or size adjustments depending on orientation.
*/
CGFloat angle = 0.0;
CGRect newFrame = [[self window] bounds];
// How to get size of status bar
// Size of status bar gets all wonky on rotations so just set it manually
// CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
CGSize statusBarSize = CGSizeMake(20.0, 20.0);
if (deviceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
angle = M_PI;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.height -= statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeLeft)
{
angle = - M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.x += statusBarSize.height;
newFrame.size.width += statusBarSize.height;
}
}
else if (deviceOrientation == UIInterfaceOrientationLandscapeRight)
{
angle = M_PI / 2.0f;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.size.width -= statusBarSize.height;
}
}
else
{
angle = 0.0;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
newFrame.origin.y += statusBarSize.height;
newFrame.size.height -= statusBarSize.height;
}
}
imageView.transform = CGAffineTransformMakeRotation(angle);
imageView.frame = newFrame;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Add background image to window with orientation changes so that it is visible in all views.
// A listener is added since subviews do not receive orientation changes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object: nil];
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[Utility getBackgroundImageNameWithOrientation:deviceOrientation]]];
[[self window] addSubview:imageView];
return YES;
}
Add the following to Utility.h
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation;
Add the following to Utility.m
+ (NSString *)getBackgroundImageNameWithOrientation:(UIDeviceOrientation)interfaceOrientation
{
NSString *imageName = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~ipad.png";
}
else
{
imageName = #"Default-Portrait~ipad.png";
}
}
else
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
imageName = #"Default-Landscape~iphone.png";
}
else
{
imageName = #"Default.png";
}
}
return imageName;
}
Windows of iOS7 have different behaviors with windows of iOS8/9.
Keyboard window of iOS7 and all windows of iOS8/9 always have correct orientation & size. So you can observer the size change events and update the frame of your view.
But other windows of iOS7 always keep the portrait orientation and size. You need update transform of your view after rotation.
You need to observer UIApplicationWillChangeStatusBarOrientationNotification and update size of your UIView like this:
#interface MyView : UIView
#end
#implementation MyView
- (instancetype)init
{
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(changeOrientationHandler:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
}
- (void)updateTransformWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
CGFloat offset = (height - width) / 2;
CGAffineTransform transform;
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIInterfaceOrientationLandscapeRight:
transform = CGAffineTransformMakeTranslation(-offset, offset);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIInterfaceOrientationPortraitUpsideDown:
transform = CGAffineTransformMakeRotation(-M_PI);
break;
default:
transform = CGAffineTransformIdentity;
break;
}
self.transform = transform;
self.frame = CGRectMake(0, 0, width, height);
}
- (void)updateFrameWithOrientation:(UIInterfaceOrientation)orientation
{
CGFloat width = CGRectGetWidth(self.window.bounds);
CGFloat height = CGRectGetHeight(self.window.bounds);
if (width > height) {
CGFloat temp = width;
width = height;
height = temp;
}
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
self.frame = CGRectMake(0, 0, height, width);
break;
default:
self.frame = CGRectMake(0, 0, width, height);
break;
}
}
- (void)updateWithOrientation:(UIInterfaceOrientation)orientation
{
BOOL isIos7 = [[UIDevice currentDevice].systemVersion floatValue] < 8.0;
BOOL isKeyboardWindow = [self.window isKindOfClass:NSClassFromString(#"UITextEffectsWindow")];
if (isIos7 == YES && isKeyboardWindow == NO) {
[self updateTransformWithOrientation:orientation];
} else {
[self updateFrameWithOrientation:orientation];
}
}
- (void)changeOrientationHandler:(NSNotification *)notification
{
[UIView animateWithDuration:0.25 animations:^{
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
[self updateWithOrientation:orientation];
}];
}
#end