详解UICoordinateSpace和UIScreen在iOS 8上的坐标问题

详解UICoordinateSpace和UIScreen在iOS 8上的坐标问题

上一文为大家带来了UIView中关于AutoLayout API的学习笔记,之后我在总结UIViewController关于AutoLayout API的时候,发现了一个坑,先拿出来分享给大家。

我分别在iOS 7 和iOS 8下运行了如下代码:

UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
BOOL landscape = (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight);
NSLog(@"Currently landscape: %@, width: %.2f, height: %.2f", 
  (landscape ? @"Yes" : @"No"), 
  [[UIScreen mainScreen] bounds].size.width, 
  [[UIScreen mainScreen] bounds].size.height);

发现在iOS 8下运行结果如下:

Currently landscape: No, width: 320.00, height: 568.00
Currently landscape: Yes, width: 568.00, height: 320.00

iOS 7运行结果如下:

Currently landscape: No, width: 320.00, height: 568.00
Currently landscape: Yes, width: 320.00, height: 568.00

UIScreen的bounds随着设备的旋转在iOS 8的情况下发生了变化。之后找了资料(具体请参考 WWDC 2014 View Controller Advancements in iOS 8这一期节目中关于Screen Coordinates),发现UIScreen在iOS 8中也是进行旋转的,具体有四点改变如下:

  • [UIScreen bounds] -> interface-oriented
  • [UIScreen applicationFrame] -> interface-oriented
  • status bar frame notifications -> interface-oriented
  • keyboard frame notificaitons -> interface-oriented

在iOS 8中苹果认为设备的旋转就是当前Screen的Bounds发生了变化,因此引入了Size Class的概念。苹果使用UITraitCollection来描述不同Size Class的信息。当TraitCollection改变时(设备发生了旋转),上一篇文章最后讲到的 - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection将会被调用。首先被触发的就是我们的UIScreen,然后逐层往下传递,具体如下:

UIScreen -> UIWindow -> UIViewController -> ChildViewControllers -> View -> Subviews

因此我们在处理上述几种情况的时候,应该留意iOS 8和iOS 8之前版本的不同,而进行区别编码。

这里分享给大家获得修正过后Screen size的方法(支持新老版本):

+ (CGSize)fixedScreenSize {
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        return CGSizeMake(screenSize.height, screenSize.width);
    } else {
        return screenSize;
    }
}
UICoordinateSpace protocol

iOS 8之前,window和screen的坐标是不变的。它们不会进行旋转,而是一直处于竖屏时的坐标体系。我们是在View Controller旋转方法中做对应的处理。

iOS8之后,像上文描述的那样,window和screen的坐标会随着app旋转而改变。也不总是这样的情况,因为View Controller仍然决定了App在当前视图下支持哪几个方向。 在某些时候,我们需要一些修正之后的frame(比如需要存储touch事件在屏幕上的坐标),这个时候新的UICoordinateSpace就登场了。

@protocol UICoordinateSpace <NSObject>

- (CGPoint)convertPoint:(CGPoint)point toCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace NS_AVAILABLE_IOS(8_0);
- (CGPoint)convertPoint:(CGPoint)point fromCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace NS_AVAILABLE_IOS(8_0);
- (CGRect)convertRect:(CGRect)rect toCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace NS_AVAILABLE_IOS(8_0);
- (CGRect)convertRect:(CGRect)rect fromCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace NS_AVAILABLE_IOS(8_0);

@property (readonly, nonatomic) CGRect bounds NS_AVAILABLE_IOS(8_0);

@end

UICoordinateSpace协议的接口如上,使用这些接口我们可以从当前视图的坐标转化到在screen的坐标体系当中,像这样:

[myView convertPoint:point toCoordinateSpace:myView.window.screen.fixedCoordinateSpace];

这里提到了fixedCoordinateSpace,在iOS 8上UIScreen提供了两个新的属性,如下:

@property (readonly) id <UICoordinateSpace> coordinateSpace NS_AVAILABLE_IOS(8_0);
@property (readonly) id <UICoordinateSpace> fixedCoordinateSpace NS_AVAILABLE_IOS(8_0);

coordinateSpace值的是当前screen旋转之后的坐标体系,fixedCoordinateSpace指的是修正之后的坐标体系。我们同样可以把screen的坐标转换到当前视图的坐标体系,如下:

[myView.window.screen.fixedCoordinateSpace convertPoint:point      toCoordinateSpace:myView];

举个例子来说,在iPhone 5的屏幕大小上(320 * 568),myView在竖屏方向的frame是CGRectMake(50, 50, 200, 200),myView的Super View是window。有如下输出结果:

CGPoint point = [myView convertPoint:CGPointMake(0, 0) toCoordinateSpace:[UIScreen mainScreen].fixedCoordinateSpace];
NSLog(@"fixedCoordinateSpace %@", NSStringFromCGPoint(point)); // {50, 50}

point = [myView convertPoint:CGPointMake(0, 0) toCoordinateSpace:[UIScreen mainScreen].coordinateSpace];
NSLog(@"coordinateSpace %@", NSStringFromCGPoint(point)); // {50, 50}

此时我们把设备顺时针旋转90度,在这两种不同的坐标体系下分别输出的结果是:

NSLog(@"fixedCoordinateSpace %@", NSStringFromCGPoint(point)); // {50, 518}

NSLog(@"coordinateSpace %@", NSStringFromCGPoint(point)); // {50, 50}

总结

我们在编码过程中,应该注意这些坐标的改变。尤其要注意不同iOS版本下,对不同行为做相应处理。顺便祈祷公司早点不支持旧版本操作系统吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值