Xamarin.iOS 中的编程布局约束

本指南介绍如何在 C# 代码中使用 iOS 自动布局约束,而不是在 iOS 设计器中创建它们

自动布局(也称为“自适应布局”)是一种响应式设计方法。 与每个元素位置都硬编码到屏幕上的某个点的过渡布局系统不同,自动布局与“关系”有关 - 元素位置相对于设计图面上的其他元素。 自动布局的核心是约束或规则的概念,这些约束或规则定义一个元素或一组元素在屏幕上其他元素的上下文中的放置。 由于元素不依赖于屏幕上的特定位置,因此约束有助于创建在不同屏幕大小和设备方向上看起来合理的自适应布局。

通常,在 iOS 中使用自动布局时,你将使用 Xcode 的 Interface Builder 以图形方式将布局约束放置在 UI 项上。 但是,有时可能需要在 C# 代码中创建和应用约束。 例如,在使用添加到 UIView 的、动态创建的 UI 元素时。

本指南介绍如何使用 C# 代码创建和使用约束,而不是在 Xcode 的 Interface Builder 中以图形方式创建约束。

以编程方式创建约束

如上所述,通常你会在 iOS 设计器中使用自动布局约束。 对于必须以编程方式创建约束的情况,可以选择以下三个选项:

  • 布局定位点 - 此 API 提供对受约束 UI 项的定位点属性(例如 TopAnchorBottomAnchorHeightAnchor)的访问。
  • 布局约束 - 可以直接使用 NSLayoutConstraint 类创建约束。
  • 视觉格式语言 - 提供类似于 ASCII 图文的方法来定义约束。

以下部分将详细介绍每个选项。

布局定位点

使用 NSLayoutAnchor 类可以创建一个流畅的界面,用于根据受约束 UI 项的定位点属性创建约束。 例如,视图控制器的顶部和底部布局引导元素会公开 TopAnchorBottomAnchorHeightAnchor 定位点属性,而视图则会公开边缘、中心、大小和基线属性。

重要

除了标准的定位点属性集之外,iOS 视图还包括 LayoutMarginsGuidesReadableContentGuide 属性。 这些属性公开 UILayoutGuide 对象,用于分别处理视图的边距和可读内容引导元素。

布局定位点提供了多种以易于阅读、紧凑格式创建约束的方法:

  • ConstraintEqualTo - 定义一种关系,其中 first attribute = second attribute + [constant] 具有选择性提供的 constant 偏移值。
  • ConstraintGreaterThanOrEqualTo - 定义一种关系,其中 first attribute >= second attribute + [constant] 具有选择性提供的 constant 偏移值。
  • ConstraintLessThanOrEqualTo - 定义一种关系,其中 first attribute <= second attribute + [constant] 具有选择性提供的 constant 偏移值。

例如:

// Get the parent view's layout
var margins = View.LayoutMarginsGuide;

// Pin the leading edge of the view to the margin
OrangeView.LeadingAnchor.ConstraintEqualTo (margins.LeadingAnchor).Active = true;

// Pin the trailing edge of the view to the margin
OrangeView.TrailingAnchor.ConstraintEqualTo (margins.TrailingAnchor).Active = true;

// Give the view a 1:2 aspect ratio
OrangeView.HeightAnchor.ConstraintEqualTo (OrangeView.WidthAnchor, 2.0f);

典型的布局约束可以简单地表达为线性表达式。 请参见以下示例:

A Layout Constraint expressed as a linear expression

使用布局定位点将其转换为以下 C# 代码行:

PurpleView.LeadingAnchor.ConstraintEqualTo (OrangeView.TrailingAnchor, 10).Active = true; 

其中 C# 代码的各部分对应于等式的给定部分,如下所示:

等式 代码
项 1 PurpleView
属性 1 LeadingAnchor
关系 ConstraintEqualTo
“乘数” 默认为 1.0,因此未指定
项 2 OrangeView
属性 2 TrailingAnchor
返回的常量 10.0

除了仅提供求解给定布局约束等式所需的参数之外,每个布局定位点方法还强制要求传递给它们的参数的类型安全性。 因此,水平约束定位点(例如 LeadingAnchorTrailingAnchor)只能与其他水平定位点类型一起使用,并且乘数仅提供给大小约束。

布局约束

可以通过在 C# 代码中直接构造 NSLayoutConstraint 来手动添加自动布局约束。 与使用布局定位点不同,必须为每个参数指定一个值,即使它对定义的约束没有影响。 结果,最终将生成大量难以阅读的样板代码。 例如:

//// Pin the leading edge of the view to the margin
NSLayoutConstraint.Create (OrangeView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, View, NSLayoutAttribute.LeadingMargin, 1.0f, 0.0f).Active = true;

//// Pin the trailing edge of the view to the margin
NSLayoutConstraint.Create (OrangeView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, View, NSLayoutAttribute.TrailingMargin, 1.0f, 0.0f).Active = true;

//// Give the view a 1:2 aspect ratio
NSLayoutConstraint.Create (OrangeView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, OrangeView, NSLayoutAttribute.Width, 2.0f, 0.0f).Active = true;

其中,NSLayoutAttribute 枚举定义视图边距的值,并对应于 LeftRightTopBottomLayoutMarginsGuide 属性,而 NSLayoutRelation 枚举定义了在给定的属性(例如 EqualLessThanOrEqualGreaterThanOrEqual)之间创建的关系。

与布局定位点 API 不同,NSLayoutConstraint 创建方法不会突出显示特定约束的重要方面,并且不会对该约束执行编译时检查。 因此,很容易构造一个无效的约束,从而在运行时引发异常。

视觉格式语言

视觉格式语言允许使用类似于 ASCII 图文的字符串来定义约束,这些字符串提供了正在创建的约束的视觉表示形式。 这种做法存在以下优点和缺点:

  • 视觉格式语言仅强制创建有效约束。
  • 自动布局使用视觉格式语言将约束输出到控制台,因此调试消息类似于用于创建约束的代码。
  • 视觉格式语言允许使用非常紧凑的表达式同时创建多个约束。
  • 由于不会对视觉格式语言字符串进行编译端验证,因此只能在运行时发现问题。
  • 由于视觉格式语言强调可视化而不是完整性,因此无法使用它来创建某些约束类型(例如比率)。

使用视觉格式语言创建约束时,请执行以下步骤:

  1. 创建一个 NSDictionary,其中包含视图对象和布局引导元素以及定义格式时要使用的字符串键。
  2. (可选)创建一个 NSDictionary,用于定义一组键和值 (NSNumber) 来用作约束的常量值。
  3. 创建格式字符串以布局单列或单行项。
  4. 调用 NSLayoutConstraint 类的 FromVisualFormat 方法来生成约束。
  5. 调用 NSLayoutConstraint 类的 ActivateConstraints 方法来激活并应用约束。

例如,若要在视觉格式语言中创建前导和尾部约束,可使用以下命令:

// Get views being constrained
var views = new NSMutableDictionary (); 
views.Add (new NSString ("orangeView"), OrangeView);

// Define format and assemble constraints
var format = "|-[orangeView]-|";
var constraints = NSLayoutConstraint.FromVisualFormat (format, NSLayoutFormatOptions.AlignAllTop, null, views);

// Apply constraints
NSLayoutConstraint.ActivateConstraints (constraints);

由于在使用默认间距时,视觉格式语言始终会创建附加到父视图边距的零点约束,因此,此代码会生成与上述示例相同的结果。

对于更复杂的 UI 设计,例如在单行上创建多个子视图,视觉格式语言将指定水平间距和垂直对齐方式。 如上面的示例所示,它指定 AlignAllTopNSLayoutFormatOptions 将行或列中的所有视图与顶部对齐。

有关指定通用约束和视觉格式字符串语法的一些示例,请参阅 Apple 的视觉格式语言附录

总结

本指南介绍了如何在 C# 中创建和使用自动布局约束,而不是在 iOS 设计器中以图形方式创建这些约束。 首先,介绍了如何使用布局定位点 (NSLayoutAnchor) 来处理自动布局。 接下来,介绍了如何使用布局约束 (NSLayoutConstraint)。 最后,演示了如何为自动布局使用视觉格式语言。