2023年7月14日发(作者:)
UIView的Margins写在前⾯之前使⽤Storyboard拖拽约束时,可以看到⽐较的view有margin选项,来⽀持相对某view的margin进⾏布局。那么在代码中如何体现,就需要UIView的以下API:layoutMarginsdirectionalLayoutMarginspreservesSuperviewLayoutMarginsiOS11引⼊了Safe Area的概念,相应对UIView的Margin也增加以下API:insetsLayoutMarginsFromSafeArealayoutMargins这个属性⽤于指定视图和它的⼦视图之间的边距。和preservesSuperviewLayoutMargins⼀起在iOS8开始引⼊。AutoLayout中NSLayoutAttribute的枚举值也有相应的更新: NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),在VFL(Visual Format Language)语法中也有相应的引⼊,⽐如“|-[subview]-|”,设置Margin约束。⼦视图采⽤上⾯的约束与⽗视图建⽴约束时,⽗视图的layoutMarigin才会⽣效。场景⼀:blueView占据全屏,它的⼦视图orangeView相对它的margin布局 UIView *blueView = [[UIView alloc] init]; oundColor = [UIColor blueColor]; atesAutoresizingMaskIntoConstraints = NO; [ addSubview:blueView]; [blueView mas_makeConstraints:^(MASConstraintMaker *make) { _equalTo(); }];
UIView *orangeView = [[UIView alloc] init]; oundColor = [UIColor orangeColor]; atesAutoresizingMaskIntoConstraints = NO; [blueView addSubview:orangeView]; [orangeView mas_makeConstraints:^(MASConstraintMaker *make) { _equalTo(_topMargin); _equalTo(_bottomMargin); _equalTo(_leftMargin); _equalTo(_rightMargin); }]; Margins = UIEdgeInsetsMake(50, 50, 50, 50);效果:可以看到orangeView相对上下左右有个⽐较⼤的margin。(这⾥⾁左右下的margin是50,但是⾁眼看距离上⾯的margin似乎要⽐左右下的margin⼤,这是因为iOS11的Safe Area,下⾯会讲到)除了layoutMargins,iOS11还增加了⼀个新属性directionalLayoutMargins,这个属性的类型不是UIEdgeInsets,⽽是NSDirectionalEdgeInsets,定义如下:typedef struct UIEdgeInsets { CGFloat top, left, bottom, right;} UIEdgeInsets;typedef struct NSDirectionalEdgeInsets { CGFloat top, leading, bottom, trailing;
} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));从结构上看主要是将UIEdgeInsets结构的left和right调整为NSDirectionalEdgeInsets结构的leading和trailing。这⼀调整主要是为了RightTo Left(RTL)语⾔下可以进⾏⾃动适配。preservesSupereservesSuperviewLayoutMargins当这个属性的值为YES的时候,⼀个视图布局内容时其⽗视图的margins也会被考虑在内。默认是NO。场景⼆:blueView占据全屏,它的⼦视图orangeView相对blueView的边距布局,orangeView的⼦视图redView相对orangeView的margin布局。 UIView *blueView = [[UIView alloc] init]; oundColor = [UIColor blueColor]; atesAutoresizingMaskIntoConstraints = NO; [ addSubview:blueView]; [blueView mas_makeConstraints:^(MASConstraintMaker *make) { _equalTo(); }];
UIView *orangeView = [[UIView alloc] init]; oundColor = [UIColor orangeColor]; atesAutoresizingMaskIntoConstraints = NO; [blueView addSubview:orangeView]; [orangeView mas_makeConstraints:^(MASConstraintMaker *make) { _equalTo(blueView); _equalTo(blueView); _equalTo(blueView).multipliedBy(0.5); }];
UIView *redView = [[UIView alloc] init]; oundColor = [UIColor redColor]; atesAutoresizingMaskIntoConstraints = NO; [orangeView addSubview:redView]; [redView mas_makeConstraints:^(MASConstraintMaker *make) { _equalTo(_topMargin); _equalTo(_bottomMargin); _equalTo(_leftMargin); _equalTo(_rightMargin); }]; Margins = UIEdgeInsetsMake(50, 50, 50, 50); vesSuperviewLayoutMargins = YES; Margins = UIEdgeInsetsZero;效果:其中vesSuperviewLayoutMargins = YES;就设置了orangeView保持⽗视图的layoutMargins,下⾯没有保持⽗视图的边距是因为,orangeView不在⽗视图的bottomMargin内。如果把vesSuperviewLayoutMargins = YES;这句代码去掉或者设置为NO,效果如下:这⾥忽略上边距(iOS11 Safe Area引起,后⾯讲),发现redView的左右边距不再相对爷爷视图,也就是blueView的Margin对齐了,这是因为视图orangeView没有保持blueView的layoutMargin。接下来我们把代码修改为: Margins = UIEdgeInsetsMake(50, 50, 50, 50); vesSuperviewLayoutMargins = YES; Margins = UIEdgeInsetsMake(0, 50, 0, 0);也就是我们想要orangeView保持⽗视图的Margin的基础上,增加⾃⼰的左Margin,效果如下:发现效果和orangeView没有⾃⼰的Margin时⼀样,这是因为视图本⾝的Margin和从⽗视图保持来的Margin是重合的。也就是说preservesSuperviewLayoutMargins=YES时,真正layoutMargins的值是,“⼿动设置layoutMargins值”与”在⽗视图Margin范围内区域“的最⼤值,伪代码表⽰如下:layoutMargins = Max(Margins, Combine(Margins, ));iOS11的insetsLayoutMarginsFromSafeArfeAreaea从iOS 7以来,我们在整个操作系统中都有这些半透明的bars,苹果⿎励我们通过这些bars绘制内容,我们是通过viewController的edgesForExtendedLayout属性来做这些的。iOS11的Safe Area的出现,很快将iOS7出现的topLayoutGuide、bottomLayoutGuide废弃。Safe Area定义了view中可视区域的部分,保证不被系统的状态栏、或⽗视图提供的view如导航栏覆盖。UIView的safeAreaInsets属性反映了⼀个view距离该view的安全区域的边距。对于⼀个Controller的根视图⽽⾔,SafeAreaInsets值包括了被statusbar和其他可视的bars覆盖的区域和其他通过additionalSafeAreaInsets⾃定义的insets值。对于view层次中得其他view,SafeAreaInsets值反映了view被覆盖的部分。如果⼀个view全部在它⽗视图的安全区域内,则SafeAreaInsets值为(0,0,0,0)。说了这么多终于到insetsLayoutMarginsFromSafeArea了,从名字就可以看出来它和layoutMargins和safeAreaInsets有⼀定联系。我们通过下⾯的场景来证明⼀下:我们想要看⼀个view在真正布局时的safeAreaInsets值和layoutMargins值,这样写⼀个UIView的⼦类TestView,重写⽗类的layoutSubviews⽅法,打印出这两个值,并把上⾯的三个视图改成TestView的实例:- (void)layoutSubviews{ [super layoutSubviews]; NSLog(@"%@", self); NSLog(@"safeAreaInsets : %@", [NSValue valueWithUIEdgeInsets:eaInsets]); //safeAreaInsets是iOS11才有的属性,注意使⽤时判断系统版本 NSLog(@"layoutMargins : %@", [NSValue valueWithUIEdgeInsets:Margins]);}将上⾯三个视图的layoutMargin设置为: Margins = UIEdgeInsetsMake(50, 50, 50, 50); Margins = UIEdgeInsetsMake(0, 50, 0, 0);控制台打印://blueViewsafeAreaInsets : UIEdgeInsets: {20, 0, 0, 0}layoutMargins : UIEdgeInsets: {70, 50, 50, 50}//orangeViewsafeAreaInsets : UIEdgeInsets: {20, 0, 0, 0}layoutMargins : UIEdgeInsets: {20, 50, 0, 0}//redViewsafeAreaInsets : UIEdgeInsets: {0, 0, 0, 0}layoutMargins : UIEdgeInsets: {8, 8, 8, 8}打印的layoutMargin和设置的layoutMargin值不⼀样,视图真正显⽰时的layoutMargin其实是设置的layoutMargin和safeAreaInsets的累加。那么跟insetsLayoutMarginsFromSafeArea属性有什么关系呢,这个值默认是YES,我们把它设置为NO: Margins = UIEdgeInsetsMake(50, 50, 50, 50); Margins = UIEdgeInsetsMake(0, 50, 0, 0); LayoutMarginsFromSafeArea = NO;控制台打印://orangeViewsafeAreaInsets : UIEdgeInsets: {20, 0, 0, 0}layoutMargins : UIEdgeInsets: {0, 50, 0, 0}发现safeAreaInsets不再累加到layoutMargins上了,所以insetsLayoutMarginsFromSafeArea属性也很简单,就是控制safeAreaInsets是否加到layoutMargins上。另外,从打印结果看,safeAreaInsets的值就是status bar的⾼度,也说明了我们之前的效果上⾯的边距要多出⼀点的原因。总结本⽂主要讲了UIView关于Margin的以下属性:layoutMargins: iOS8开始引⼊,⽤于指定视图和它的⼦视图之间的边距。directionalLayoutMargins:iOS11开始引⼊,可以根据语⾔的⽅向进⾏前后布局,与layoutMargins相⽐,能更好的适配RTL语⾔。preservesSuperviewLayoutMargins:iOS8开始引⼊,当这个属性的值为YES的时候,⼀个视图布局内容时其⽗视图的margins也会被考虑在内。默认是NO。insetsLayoutMarginsFromSafeArea:iOS11开始引⼊,控制safeAreaInsets是否加到layoutMargins上。默认YES。他们之间的关系:第⼀步:⼀个视图“真正的layoutMargins”是否受⽗视图的layoutMargins影响,取决于preservesSuperviewLayoutMargins值,如果NO,则不考虑⽗视图layoutMargins,如果YES,受影响值为视图在⽗视图的margin的区域,然后取“设置的layoutMargins”与“在⽗视图的margin区域”的最⼤值。第⼆步:再判断视图是否受Safe Area影响,判断insetsLayoutMarginsFromSafeArea值,如果NO,直接使⽤,如果YES,则将从上⾯得到的layoutMargins加上safeAreaInsets(注意这⾥是加上,前⾯与⽗视图margin的影响区域是取最⼤值),得到最终真正的layoutMargins。伪代码表⽰如下:- (UIEdgeInsets)getRealLayoutMargins { UIEdgeInsets layoutMargins = UIEdgeInsetsMake(8, 8, 8, 8); //默认是8 if (vesSuperviewLayoutMargins) { layoutMargins = Max(settingsLayoutMargins, Combine(Margins, )); } if (LayoutMarginsFromSafeArea) { layoutMargins = Add(layoutMargins, eaInsets); } return layoutMargins;}参考layoutMargins和preservesSuperviewLayoutMargins
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689264789a226438.html
评论列表(0条)