【iOS】iPhoneX旋屏导致autoresizingMask计算错误的问题及解决方案

博客探讨了在iPhoneX上旋转屏幕时,由于autoresizingMask的局限性导致的视图布局错误。内容详细展示了竖屏、旋转后以及再次旋转时的视图状态,并分析了问题原因。提出使用autoLayout作为解决方案,介绍了autoLayout的基本概念,以及如何通过封装简化布局代码,以避免autoresizingMask的限制,促进向autoLayout的过渡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个问题看图会比较直观

竖屏情况下:

屏幕旋转后:

重新设置为竖屏:

再次旋转为横屏:

下面分析一下View的设置

红色背景productView是一个普通UIView

self.clipsToBounds = YES;
self.backgroundColor = [UIColor redColor];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;

版本Label和人气Label直接add到productView上,使用frame来定位

self.versionLabel = [[UILabel alloc] initWithFrame:CGRectMake(25, 0.f, CGRectGetWidth(self.frame) / 2.0f - 25 - 5.f, 16.f)];
self.versionLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;

self.usePersonLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.frame) / 2 + 5.f, CGRectGetMinY(self.versionLabel.frame), CGRectGetWidth(self.frame) / 2.0f - 25 - 5.f, CGRectGetHeight(self.versionLabel.frame))];
self.usePersonLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;

可以看到其实预期的效果应该是:

问题出在autoresizingMask的局限性:不能实现等分行为

这就造成了无论怎么组合autoresizingMask的属性,都不可能完美达到这个效果

也因此其实在非iPhoneX上这套实现也是存在瑕疵的(表现和iPhoneX第一次旋转时相同,可以明显看到旋屏之后的错位,但不会出现iPhoneX那种严重的重合问题)

iPhoneX第二次旋屏之后的问题十分严重,autoresizingMask的三项组合导致其frame的计算完全错误,Label完全重合在一起

(开发者Label和更新时间Label同理)

两个问题其实都是因为autoresizingMask,因此想到使用autoLayout 取代autoresizingMask进行布局设置


简单讲一下autoLayout的概念。(网上已经有很多资料了)

我个人理解最重要的一点是:每个view的布局不再由自己决定了,而是变为受到父view和其他view的约束

view的属性=约束view的view的属性*multiplier+constant常量

比如原来的view.frame.origin.x,就可以理解为是view左边界和父view左边界,转化为代码

NSLayoutConstraint * leftConstraint;
leftConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft 
    relatedBy:NSLayoutRelationEqual toItem:self
    attribute: NSLayoutAttributeLeft multiplier:1.0 constant:view.frame.origin.x];

那等分宽度也就很好理解了,ratio = 1/等分数(二等分就是0.5)

NSLayoutConstraint * widthConstraint;
widthConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth 
    relatedBy:NSLayoutRelationEqual toItem:self 
    attribute:NSLayoutAttributeWidth multiplier:ratio constant:0];

有一点要注意不过也很好理解,应该把所有view都加进父view再添加约束。


autoLayout和frame+autoresizingMask的理念完全不同,二者不可以对一个view同时使用,但是嵌套并不会有影响,因此考虑写一个baseview,针对需要等分的view替换其原有的布局方式。

#import <UIKit/UIKit.h>

typedef NS_ENUM(UInt32, FLAlignmentDirection) {
    FLAlignmentDirection_Horizontal = 0,   //横向
    FLAlignmentDirection_Vertical = 1,     //纵向
};

@interface FLAutoLayoutBaseView : UIView

@property(nonatomic,assign,readonly) BOOL hadAutolayout;//判断是否全部的subview都进行了autolayout
@property(nonatomic,strong,readonly) NSMutableArray<UIView *> *subviews;

- (instancetype)initWithFrame:(CGRect)frame alignmentDirection:(FLAlignmentDirection)direction;//只允许在init时设置frame

//添加subview 添加顺序决定排列顺序
//view.frame的x,y需要设置
//横向排列时width需要设置 纵向排列时height需要设置
- (void)addSubview:(UIView *)view;

//以下autoLayout方式在addSubView之后调用
- (void)autoLayoutByDefault;//采用默认的约束方式 根据横纵向等分宽度或者高度

@end

把baseview写的尽可能简单,仅提供了等分的功能(实际上还能提供完全替代autoresizingMask的接口)

值得一提的是重写addSubview的行为

- (void)addSubview:(UIView *)view //记录add入FLAutoLayoutBaseView的view 只能按序添加
{
    [super addSubview:view];
    self.maskHadAutoLayout = [self.maskHadAutoLayout stringByAppendingString:@"0"];
    [self.subviews addObject:view];
    //检查并处理width,height数据
    if(self.alignmentDirection == FLAlignmentDirection_Horizontal) {//横向排列则检查高度
        if(0 == CGRectGetHeight(view.frame)) //如果没有高度则设置为self的高度
            view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, self.frame.size.height);
    }
    else { //纵向排列则检查宽度
        if(0 == CGRectGetWidth(view.frame)) //如果没有宽度则设置为self的宽度
            view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, self.frame.size.width, view.frame.size.height);
    }
    
    if(NO == self.callDefaultLayout) {
        self.callDefaultLayout = YES;
        dispatch_async(dispatch_get_main_queue(), ^{
            [self autoLayoutByDefault];
        });
    }
}

最后一个判断是为了保证“应该把所有view都加进父view再添加约束,但是忘了添加约束”的情况下能够进行默认的autoLayout布局。

总的来说autoLayout不进行封装的话代码量其实会多很多,但是功能也的确比autoresizingMask完善,因此我更加偏向于逐步完善对autoLayout的封装,推广使用autoLayout。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值