MKLayoutLibrary 1.0.0

MKLayoutLibrary 1.0.0

测试测试
语言语言 Obj-CObjective C
许可证 MIT
发布最后发布2014年12月

Martin Kloeppner维护。



  • 作者:
  • mkloeppner

MKLayoutLibrary是为了方便iOS开发者设计具有动态和复杂布局的应用程序而编写的。

它提供了一些默认的布局功能,例如线性布局和堆叠布局。

MKLayoutLibrary被设计成可以根据与UIKit协作的方式进行模块化,并提供一个易于使用API进行自定义布局,以自定义布局属性进行扩展。

用法

布局视图

与MKLayoutLibrary协作有两种不同的方法

  • 创建自定义视图类(推荐)
  • 在视图控制器中使用布局

自定义类

MKLayout库被设计为可以在UIView子类中使用。

以下是一个垂直线性布局视图的例子

MKVerticalLayoutView.h

@interface MKVerticalLayoutView : UIView

@property (weak, nonatomic, readonly) UILabel *titleLabel;
@property (weak, nonatomic, readonly) UITextView *descriptionTextView;

@end

MKVerticalLayoutView.m

#import "MKVerticalLayoutView"

@interface MKVerticalLayoutView ()

@property (strong, nonatomic) MKLinearLayout *layout;

@property (weak, nonatomic) MKLinearLayoutItem *titleItem;
@property (weak, nonatomic) MKLinearLayoutItem *descriptionItem;

@end

@implementation

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
      _layout = [[MKLinearLayout alloc] init];
      _layout.view = self;
      _layout.orientation = MKLinearLayoutOrientationVertical;
      [self createLayout];
    }
    return self;
}

- (void)createLayout
{
    // Adds the view to the layout and automatically to the layouts associated view
    UILabel *titleLabel = [[UILabel alloc] init];
    self.titleItem = [self.layout addSubview:titleLabel];

    // Once the layout items exists, it can be used to configure layout specifics such as paddings or sizes
    self.titleItem.padding = UIEdgeInsetsMake(1.0f, 1.0f, 1.0f, 1.0f);

    // Specifies 30 px height but streches the width to the full available layout size so it matches the parent view
    self.titleItem.size = CGSizeMake(kMKLayoutItemSizeValueMatchParent, 30.0f);


    UITextView *descriptionTextView = [[UITextView alloc] init];
    self.descriptionItem = [self.layout addSubview:descriptionTextView];

    // Specifies that the item gots all available left space.
    // Weight and size can be used at the same time. See MKLayoutItem and MKLinearLayoutItem for further details.
    self.titleItem.weight = 1.0f;
}

- (void)layoutSubviews
{
    [self.layout layout];
}

- (UILabel *)titleLabel
{
  return (UILabel *)self.titleLabelItem.view;
}

- (UITextView *)descriptionTextView
{
  return (UITextView *)self.descriptionItem.view;
}

@end

任何其他组合

NKLayout可以在任何与UIKit协作的环境中使用,但UIView的方法 - (void)layoutSubviews完美地管理了视图需要布局的时间。当然,可以在任何时间调用 - (void)layout。

子布局

MKLayout也支持子布局

// Create a layout instance
MKStackLayout *iconLayout = [[MKStackLayout alloc] init];

  UIImageView *backgroundPatternImage = [[UIImageView alloc] init];
  // Work with the sublayout as usal
  MKStackLayoutItem *backgroundPatternImage = [iconLayout addSubview:iconLayout];

// Add the sublayout to the root layout
MKLinearLayoutItem *iconLayoutLayoutItem = [self.layout addSublayout:iconLayout];

与MKLayoutLibrary协作

MKLayoutLibrary主要围绕MKLayoutItem及其相关子类。

想象一下布局作为一组盒子,这些盒子可以包含一个视图或被标识为子布局的布局。这些盒子将代表MKLayoutItem。盒子中的每个视图或子布局的位置将由布局的逻辑来确定,例如,在线性布局中每个盒子的位置遵循另一个盒子的帧或者在堆叠布局中它们会重叠。

然而,MKLayoutItem指定了一些用于调整和配置盒子布局行为的属性。

直接在MKLayoutItem中指定的所有属性都将并且需要被每个布局实现所支持。MKLayoutItem的特定子类可以提供每个布局配置的属性。

本节将介绍最重要的属性及其在所有布局中的一致行为。

大小

尺寸是指垂直和水平方向上的一个指定长度。它只能受布局项内边距的影响。否则,视图的尺寸必须与布局项的尺寸匹配。

此配置的项目必须导致视图.bounds.size具有10个单位的宽度和10个单位的高度

item.size = CGSizeMake(10.0f, 10.0f);

内边距

内边距是唯一可以并且必须影响布局项视图或子布局的最终边界矩形的属性。

以下配置的项目必须导致视图.bounds.size具有9个单位的宽度和9个单位的高度

item.size = CGSizeMake(10.0f, 10.0f);
item.padding = UIEdgeInsetsMake(0.0f, 0.0f, 1.0f, 1.0f); // Just shrink the view by reducing width and height

以下配置的项目必须导致视图.bounds.size具有9个单位的宽度和9个单位的高度,并且必须将结果框架沿x方向平移1个单位和沿y方向平移1个单位

  item.size = CGSizeMake(10.0f, 10.0f);
  item.padding = UIEdgeInsetsMake(1.0f, 1.0f, 0.0f, 0.0f); // Shrink the view and move it by 1 point on x and y axis

下一个配置是前一个配置的组合,必须导致视图.bounds.size具有8个单位的宽度和8个单位的高度,并且必须将结果框架沿x方向平移1个单位和沿y方向平移1个单位

item.size = CGSizeMake(10.0f, 10.0f);
item.padding = UIEdgeInsetsMake(1.0f, 1.0f, 1.0f, 1.0f); // Shrink the view on all edges and move it

内边距和偏移量

应该假设内边距是向外部盒子的内缩。如果只需要移动一个视图以确保抵消到特定边界的布局项,则偏移可能是一种更合理的方法。以下是一个例子

这是一个应该表示为偏移的内边距定义:因此,这些行

UIEdgeInsets padding = UIEdgeInsetsMake(0.0f, 0.0f, 5.0f, 5.0f); // Define padding to calculate the overall item size
item.gravity = MKLayoutGravityTop | MKLayoutGravityRight;
item.padding = padding;
item.size = CGSizeMake(30.0f + padding.right, 30.0f + padding.bottom); // Ensure the 30.0f pixels size by splitting up the padding definition

可以重构为这个

item.gravity = MKLayoutGravityTop | MKLayoutGravityRight;
item.offset = UIOffsetMake(-5.0f, -5.0f);

重力

如简介中所述,布局项代表了可以包含视图或子布局的盒子。这些盒子告诉布局哪块空间被指定布局项预留。根据布局实现的差异,视图的尺寸可能小于布局项预留的空间。因此,重力是有意义的,它可以让项目的视图或子布局粘附到项目的选定边缘。

因此,重力是一个掩码,允许我们

  • 垂直轴中心粘附
  • 水平中心粘附
  • 粘附到顶部
  • 粘附到左边
  • 粘附到底部
  • 粘附到右边
  • 以及这些选项中所有合理的组合

重力必须在移动视图或子布局时保留布局项内边距提供的任何偏移量。

偏移量

在调整尺寸、内边距和重力之后,偏移量提供我们移动最终视图或子布局的功能。

偏移量有助于我们指定项目框的边缘和项目视图之间的填充。

此示例将布局项视图在右边和底部移动5个单位

item.offset = UIOffsetMake(5.0f, 5.0f);

此示例将布局项视图在顶部和左边移动5个单位

item.offset = UIOffsetMake(-5.0f, -5.0f);

最佳实践

以下是一些使用MKLayout的建议。

视图属性

如果您要在运行时编辑布局,MKLayout与将MKLayoutItems作为属性而不是子视图本身一起使用效果最佳。所以Instead of

@interface MKSampleLayoutView ()

@property (weak, nonatomic) UILabel *titleLabel;

@end

使用

@interface MKSampleLayoutView ()

@property (weak, nonatomic) MK<LayoutType>LayoutItem *<descriptiveName>Item;

@end

可以访问项目视图或子视图的属性

MKLayoutItem.h

- (UIView *)view
- (MKLayout *)sublayout;

可以编写一些访问器方法来访问一些方法

- (UILabel *)titleLabel
{
    return (UILabel *)self.titleLabelLayoutItem.subview;
}

界面设计

MKLayoutLibrary基于MKLayoutMKLayoutItem。这两个类提供了管理布局层次结构的实现。以下是这两个类的具体说明。

MKLayoutItem

布局项是一个包含视图或部分布局的对象,可由MKLayout的任何子类管理。此外,MKLayoutItem本身提供了所有布局都需要支持的属性。 MKLayoutItem可以被进一步继承以提供特定布局的属性。

布局特定项的命名方案如下:MKItem。例如,MKLinearLayout与MKLinearLayoutItem一起工作。

MKLayoutItem消除了与特定UIView子类交互的需要,这使得开发者可以将MKLayoutLibrary以扩展现有代码的方式关联,而无需重构其基类和结构。

提示:子类应该包含MKLayoutItem_SubclassAccessors以使用默认初始化器。

MKLayout

MKLayout是MKLayoutLibrary的基类,需要被继承以提供特定的布局行为。该类本身的目的在于建立布局项的对象树并将其转换为UIKits的UIView层次结构。

这也是一个分享用于布局目的的计算代码的好地方。