iOS 布局方式汇总

    xiaoxiao2021-12-10  16

    作为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

    转载请注明原文地址: https://ju.6miu.com/read-700215.html

    最新回复(0)