iOS 布局方式汇总

作为iOS UI开发人员,为了适配多种机型以及横竖屏,【布局】的概念是再熟悉不过的,iOS的布局发展到今天主要有以下几种方式:

1.Absolutely Position

2.AutoResizing

3.NSLayoutConstraint

4.UIStackView

 

为了布局,就要考虑App支持的设备方向和各控件间的位置关系。根据这两个限制条件来讨论下面的技术点:

 

Absolutely Position

 

如果控件的位置是可以根据固定的控件可计算的,那么使用绝对位置是没有什么问题的,例如代码:

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.bounds = CGRectMake(0, 0, 50, 50);
[self.view addSubview:button];

 

那么,其他一些控件可以根据这个button的frame属性来动态适配frame

 

优点:

简单而快速

 

缺点:

修改比较困难

 

AutoResizing

 

这是UIView类的属性,当父控件发生变化,这个属性可以决定子控件如何调整自己的size。它是一个整数位掩码。

它的枚举结构:

typedef enum UIViewAutoresizing : NSUInteger {
    UIViewAutoresizingNone = 0,
    UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
    UIViewAutoresizingFlexibleWidth = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin = 1 << 3,
    UIViewAutoresizingFlexibleHeight = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
} UIViewAutoresizing;

 

每一个单词中都有“Flexible”这个单词,意味着,你用了某个枚举,当父控件发生变化之后,子空间对应的属性可以resize self去适配父控件,举例如下:(OC)

 

self.testAutoSizingMaskParentView = [[UIView alloc] initWithFrame:CGRectMake(105, 205, 100, 100)];
    [self.view addSubview:_testAutoSizingMaskParentView];
    
    UIView *testAutoSizingMaskView = [[UIView alloc] initWithFrame:CGRectMake(5, 5, 80, 80)];
    testAutoSizingMaskView.backgroundColor = [UIColor grayColor];
    // 这表明,当父控件发生变化的时候,允许resize self的宽度和高度以适应父控件的变化
    testAutoSizingMaskView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;

    [self.testAutoSizingMaskParentView addSubview:testAutoSizingMaskView];

// iOS 8之后的设备方向发生变化的回调
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
	// 修改父控件frame
    if (size.width > size.height) {
        self.testAutoSizingMaskParentView.frame = CGRectMake(105, 205, 50, 60);
    } else {
        self.testAutoSizingMaskParentView.frame = CGRectMake(105, 205, 100, 100);
    }
}

NSLayoutConstraint

 

这个是iOS6之后的新特性,约束,定义两个用户界面对象之间必须满足基于约束的布局系统的关系

使用Layout布局的前提要关闭控件的translatesAutoresizingMaskIntoConstraints属性,以防止系统将autoresizing mask转换到Auto Layout constraints,从而让布局出现问题。

使用NSLayoutConstraint有两种方式:

1.VFL(Visual Formate Language)

+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *,id> *)views;

 

功能        表达式

 

水平方向          H:

垂直方向          V:

Views         [view]

SuperView      |

关系         >=,==,<=

间隔              -

优先级        @value

//这个地方不要定义Frame,下面的Visual Format才是定义Frame相关的地方
UIView *redView = [[UIView alloc] init]; 
    redView.backgroundColor = [UIColor redColor];

//为了不让Constraint与View本身的autoresize冲突
    [redView setTranslatesAutoresizingMaskIntoConstraints:NO]; 
    [self.view addSubview:redView];
    
    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    [blueView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.view addSubview:blueView];
    
     //绑定两个View相关
    NSDictionary *views = NSDictionaryOfVariableBindings(redView, blueView);

    //此处的constraint是为了定义Frame水平方向相关(x, width)
    [self.view addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(>=10)-[redView(200)]"  options:0  metrics:nil views:views]];

    //此处的constraint是为了定义Frame竖直方向相关(y, height)
    [self.view addConstraints:  [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=240)-[redView(100)]"  options:0  metrics:nil views:views]];
    [self.view addConstraints:  [NSLayoutConstraint constraintsWithVisualFormat:@"H:[blueView(==redView)]" options:0 metrics:nil views:views]];
    [self.view addConstraints:  [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(40)-[blueView(==redView)]" options:0 metrics:nil views:views]];

// 实现子空间居中
NSDictionary *dic = @{@"centerLabel":centerLabel, @"superView":label};
 [label addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[centerLabel(10)]-(<=1)-[superView]" options:NSLayoutFormatAlignAllCenterY metrics:nil views:dic]];
 [label addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[centerLabel(10)]-(<=1)-[superView]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:dic]];
// 快速实现关联关系的控件的大小一样
[label(button)],这表明label的大小与button一样

 

2.指定属性关系

+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

 这个API实际就是一个数学公式:

view1.attr1 <relation> multiplier × view2.attr2 + c

 

其中支持Layout的属性的枚举如下:

typedef enum NSLayoutAttribute : NSInteger {
    NSLayoutAttributeLeft = 1,
    NSLayoutAttributeRight,
    NSLayoutAttributeTop,
    NSLayoutAttributeBottom,
    NSLayoutAttributeLeading,
    NSLayoutAttributeTrailing,
    NSLayoutAttributeWidth,
    NSLayoutAttributeHeight,
    NSLayoutAttributeCenterX,
    NSLayoutAttributeCenterY,
    NSLayoutAttributeLastBaseline,
    NSLayoutAttributeBaseline = NSLayoutAttributeLastBaseline,
    NSLayoutAttributeFirstBaseline,
    NSLayoutAttributeLeftMargin,
    NSLayoutAttributeRightMargin,
    NSLayoutAttributeTopMargin,
    NSLayoutAttributeBottomMargin,
    NSLayoutAttributeLeadingMargin,
    NSLayoutAttributeTrailingMargin,
    NSLayoutAttributeCenterXWithinMargins,
    NSLayoutAttributeCenterYWithinMargins,
    NSLayoutAttributeNotAnAttribute = 0
} NSLayoutAttribute;

 

其中支持Layout的Relation的枚举如下:

typedef enum NSLayoutRelation : NSInteger {
    NSLayoutRelationLessThanOrEqual = -1,    // <=
    NSLayoutRelationEqual = 0,		     // ==
    NSLayoutRelationGreaterThanOrEqual = 1   // >=
} NSLayoutRelation;

 

UIStackView

为UI提供了线性布局,不管是以行Or列的形式,很像Android的布局类。可以将其看作一个布局类。增强了Auto Layout的能力.

通过Arranged Subviews来管理控件,通过设置 axis(坐标轴,水平或者是垂直布局), distribution(分布方式,类似填充样式), alignment, spacing,isLayoutMarginsRelativeArrangement,isBaselineRelativeArrangement属性来实现布局的控制。

 

举例如下:(swift)

class ULable: UILabel {
override func sizeThatFits(_ size: CGSize) -> CGSize {
        return CGSize.init(width: 100, height: 100)
    }
}

let label1 = ULable()
        label1.textAlignment = .center
        label1.sizeToFit()
        label1.text = "1111111"
        label1.backgroundColor = UIColor.brown
        let label2 = UILabel()
        label2.text = "22222222"
        label2.backgroundColor = UIColor.yellow
        let sv1 = UIStackView(arrangedSubviews: [label1,label2])
        sv1.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 200)
        sv1.axis = .vertical
        sv1.spacing = 10
        sv1.alignment = .center
        sv1.distribution = .fillEqually
        self.view.addSubview(sv1)

 

补充:

也可以使用如下方式实现控件的自适应,但不推荐:

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
+ (void)autoLayoutView:(UIView *)view;
@end

#import "AppDelegate.h"

#define kDeviceScreenWidth [[UIScreen mainScreen] bounds].size.width
#define kDeviceScreenHeight [[UIScreen mainScreen] bounds].size.height

@interface AppDelegate ()

@property CGFloat autoSizeScaleX;
@property CGFloat autoSizeScaleY;

@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    if (kDeviceScreenHeight > 480) {
        self.autoSizeScaleX = kDeviceScreenWidth/320;
        self.autoSizeScaleY = kDeviceScreenHeight/568;
    } else {
        self.autoSizeScaleX = 1.0f;
        self.autoSizeScaleY = 1.0f;
    }
    return YES;
}
+ (void)autoLayoutView:(UIView *)view {
    for (UIView *v in view.subviews) {
        v.frame = autoLayoutCGRectMake(v.frame.origin.x, v.frame.origin.y, v.bounds.size.width, v.bounds.size.height);
        if (v.subviews.count > 0) {
            [AppDelegate autoLayoutView:v];
        }
    }
}

CG_INLINE CGRect autoLayoutCGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) {
    AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    CGRect rect;
    rect.origin.x = x*appDelegate.autoSizeScaleX;
    rect.origin.y = y*appDelegate.autoSizeScaleY;
    
    rect.size.width = width*appDelegate.autoSizeScaleX;
    rect.size.height = height*appDelegate.autoSizeScaleY;
    
    return rect;
    
}
@end

 

以上就是iOS的布局方式的汇总,请根据情况使用。

AutoLayout Cook Book

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值