Xamarin 中的统一情节提要Unified Storyboards in Xamarin.iOS

iOS 8 包含一种用于创建用户界面(统一情节提要)的全新、易用的机制。iOS 8 includes a new, simpler-to-use mechanism for creating the user interface — the unified storyboard. 使用单个情节提要来涵盖所有不同的硬件屏幕大小,可以在 "设计一次,使用-多" 样式中创建快速、响应性视图。With a single storyboard to cover all of the different hardware screen sizes, fast and responsive views can be created in a "design-once, use-many" style.

由于开发人员不再需要为 iPhone 和 iPad 设备创建单独的特定情节提要,因此它们可以灵活地使用通用接口设计应用程序,然后针对不同大小的类自定义该接口。As the developer no longer needs to create a separate and specific storyboard for iPhone and iPad devices, they have the flexibility to design applications with a common interface and then customize that interface for different size classes. 通过这种方式,可以将应用程序调整为每个外形规格的优点,并调整每个用户界面以提供最佳体验。In this way, an application can be adapted to the strengths of each form factor and each user interface can be tuned to provide the best experience.

大小类Size Classes

在 iOS 8 之前,开发人员使用 UIInterfaceOrientationUIInterfaceIdiom 来区分纵向模式和横向模式,以及在 iPhone 和 iPad 设备之间进行区分。Prior to iOS 8, the developer used UIInterfaceOrientation and UIInterfaceIdiom to differentiate between portrait and landscape modes, and between iPhone and iPad devices. 在 iOS8 中,通过使用大小类确定方向和设备。In iOS8, orientation and device is determined is by using Size Classes.

设备是按大小类定义的,在垂直和水平轴中,在 iOS 8 中有两种类型的类:Devices are defined by Size Classes, in both vertical and horizontal axes, and there are two types of size classes in iOS 8:

  • 常规–这适用于大屏幕尺寸(如 iPad)或小尺寸(如 UIScrollView)的小工具。Regular – This is for either a large screen size (such as an iPad) or a gadget that gives the impression of a large size (such as a UIScrollView
  • Compact –适用于较小的设备(如 iPhone)。Compact – This is for smaller devices (such as an iPhone). 此大小会考虑设备的方向。This size takes into account the orientation of the device.

如果将两个概念一起使用,则结果为 2 x 2 网格,该网格定义可在不同方向上使用的不同可能大小,如下图所示:If the two concepts are used together, the result is a 2 x 2 grid that defines the different possible sizes that can be used in both the differing orientations, as seen in the following diagram:

开发人员可以创建一个视图控制器,该控制器使用可能导致不同布局的四种可能性中的任何一种(如上图所示)。The developer can create a View Controller that uses any of the four possibilities that would result in different layouts (as seen in the graphics above).

iPad 大小类iPad Size Classes

由于大小的原因,iPad 具有适用于这两个方向的常规类大小。The iPad, due to the size, has a regular class size for both orientations.

iPhone 大小类iPhone Size Classes

IPhone 根据设备的方向提供不同大小的类:The iPhone has different size classes based on the orientation of the device:

  • 当设备处于纵向模式时,屏幕会水平和垂直地显示精简When the device is in portrait mode, the screen has a compact class horizontally and regular vertically
  • 当设备处于横向模式时,屏幕类会从纵向模式反转。When the device is in landscape mode, the screen classes are reversed from portrait mode.

iPhone 6 加上大小类iPhone 6 Plus Size Classes

当纵向时,大小与之前的 Iphone 相同,但在横向不同:The sizes are the same as the earlier iPhones when in portrait orientation, but different in landscape:

由于 iPhone 6 Plus 具有足够大的屏幕,因此它能够在横向模式下具有常规的宽度大小类。Because the iPhone 6 Plus has a large enough screen, it is able to have a Regular Width Size Class in the Landscape mode.

支持新的屏幕缩放Support for a New Screen Scale

IPhone 6 Plus 使用屏幕缩放系数为3.0 (原始 iPhone 屏幕分辨率的三倍)的新 Retina 高清显示器。The iPhone 6 Plus uses a new Retina HD display with a screen scale factor of 3.0 (three times the original iPhone screen resolution). 若要在这些设备上提供可能的最佳体验,请包括为此屏幕缩放设计的新图稿。To provide the best possible experience on these devices, include new artwork designed for this screen scale. 在 Xcode 6 及更高版本中,资产目录可以包含以1x、2x 和3倍速大小的图像;只需添加新的图像资产,在 iPhone 6 Plus 上运行时,iOS 就会选择正确的资产。In Xcode 6 and above, asset catalogs can include images at 1x, 2x, and 3x sizes; simply add the new image assets and iOS will choose the correct assets when running on an iPhone 6 Plus.

IOS 中的映像加载行为还识别了图像文件上的 @3x 后缀。The image loading behavior in iOS also recognizes an @3x suffix on image files. 例如,如果开发人员在应用程序捆绑包中包括以下文件名的图像资产(采用不同的分辨率): MonkeyIcon.pngMonkeyIcon@2x.pngMonkeyIcon@3x.pngFor example, if the developer includes an image asset (at different resolutions) in the application's bundle with the following file names: MonkeyIcon.png, MonkeyIcon@2x.png, and MonkeyIcon@3x.png. 当开发人员使用以下代码加载图像时,将自动使用 iPhone 6 上的 MonkeyIcon@3x.png 映像:On the iPhone 6 Plus the MonkeyIcon@3x.png image will be used automatically if the developer loads an image using the following code:

UIImage icon = UIImage.FromFile("MonkeyImage.png");

或者,如果使用 iOS 设计器将图像分配给 UI 元素,则在 iPhone 6 Plus 上将再次自动使用 MonkeyIcon@3x.png MonkeyIcon.pngOr if they assign the image to a UI element using the iOS Designer as MonkeyIcon.png, the MonkeyIcon@3x.png will be used, again automatically, on the iPhone 6 Plus.

动态启动屏幕Dynamic Launch Screens

启动屏幕文件在启动 iOS 应用程序时显示为初始屏幕,以便向用户提供对应用实际启动的反馈。The launch screen file is displayed as a splash screen while an iOS application is launching to provide feedback to the user that the app is actually starting-up. 在 iOS 8 之前,开发人员必须为应用程序运行的每种设备类型、方向和屏幕分辨率包含多个 Default.png 的图像资产。Prior to iOS 8, the developer would have to include multiple Default.png image assets for each device type, orientation and screen resolution that the application would be running on.

对于 iOS 8,开发人员可在 Xcode 中创建单个原子 .xib 文件,该文件使用自动布局和大小类来创建可用于每个设备、分辨率和方向的动态启动屏幕New to iOS 8, the developer can create a single, atomic .xib file in Xcode that uses Auto Layout and Size Classes to create a Dynamic Launch Screen that will work for every device, resolution and orientation. 这不仅减少了开发人员创建和维护所有必需的图像资产所需的工作量,而且还减少了应用程序的已安装捆绑的大小。This not only reduces the amount of work required of the developer to create and maintain all the required image assets, but it reduces the size of the application's installed bundle.

TraitsTraits

特性是一些属性,可用于确定在其环境更改时布局的变化方式。Traits are properties that can be used to determine how a layout changes as its environment changes. 它们由一组属性(根据 UIUserInterfaceSizeClass``HorizontalSizeClassVerticalSizeClass)以及接口用法(UIUserInterfaceIdiom)和显示比例组成。They consist of a set of properties (the HorizontalSizeClass and VerticalSizeClass based on UIUserInterfaceSizeClass), as well as the interface idiom ( UIUserInterfaceIdiom) and the display scale.

所有上述状态都在 Apple 引用为特征集合(UITraitCollection)的容器中打包,其中不仅包含属性,还包含其值。All of the above states are wrapped up in a container that Apple refers to as a Trait Collection ( UITraitCollection), which contains not only the properties but their values as well.

特征环境Trait Environment

特性环境是 iOS 8 中的新接口,能够返回以下对象的特征集合:Trait Environments are a new interface in iOS 8 and are able to return a Trait Collection for the following objects:

  • 屏幕(UIScreens)。Screens ( UIScreens ).
  • Windows (UIWindows)。Windows ( UIWindows ).
  • 查看控制器(UIViewController)。View Controllers ( UIViewController ).
  • 视图(UIView)。Views ( UIView ).
  • 表示控制器(UIPresentationController)。Presentation Controller ( UIPresentationController ).

开发人员使用特性环境返回的特征集合来确定应如何布置用户界面。The developer uses the Trait Collection returned by a Trait Environment to determine how a user interface should be laid out.

所有特征环境都使层次结构如下图所示:All of the Trait Environments make a hierarchy as seen in the following diagram:

默认情况下,上述每个特征环境的特征集合将从父环境流向子环境。The Trait Collection that each of the above Trait Environments have will flow, by default, from the parent to the child environment.

除了获取当前特征集合外,特征环境还具有 TraitCollectionDidChange 方法,该方法可在视图或视图控制器子类中被重写。In addition to getting the current Trait Collection, the Trait Environment has a TraitCollectionDidChange method, which can be overridden in the View or View Controller subclasses. 开发人员可以使用此方法来修改依赖于这些特征发生更改的特征的任何 UI 元素。The developer can use this method to modify any of the UI elements that depend on traits when those traits have changed.

典型特征集合Typical Trait Collections

本部分介绍用户在使用 iOS 8 时将遇到的典型类型的特征集。This section will cover the typical types of trait collections that the user will experience when working with iOS 8.

下面是开发人员在 iPhone 上可能看到的典型特征集合:The following is a typical Trait Collection that the developer might see on an iPhone:

PropertyProperty “值”Value
HorizontalSizeClass CompactCompact
VerticalSizeClass 规则Regular
UserInterfaceIdom 电话Phone
DisplayScale 2.02.0

上述集将表示完全限定的特征集,因为它具有其所有特征属性的值。The above set would represent a Fully Qualified Trait Collection, as it has values for all of its trait properties.

还可以具有一个特征集合,其中缺少某些值(Apple 将其称为未指定):It is also possible to have a Trait Collection that is missing some of its values (which Apple refers to as Unspecified):

PropertyProperty “值”Value
HorizontalSizeClass CompactCompact
VerticalSizeClass 未指定Unspecified
UserInterfaceIdom 未指定Unspecified
DisplayScale 未指定Unspecified

不过,通常情况下,当开发人员为特征集合请求特征环境时,它将返回完全限定的集合,如以上示例中所示。Generally, however, when the developer asks the Trait Environment for its Trait Collection, it will return a fully qualified collection as seen in the example above.

如果特性环境(如视图或视图控制器)不在当前视图层次结构中,开发人员可能会获得一个或多个特征属性的未指定值。If a Trait Environment (like a View or View Controller) is not inside of the current view hierarchy, the developer might get back unspecified values for one or more of the trait properties.

如果开发人员使用 Apple 提供的一种创建方法(如 UITraitCollection.FromHorizontalSizeClass)来创建新的集合,则开发人员还将获得部分限定的特征集合。The developer will also get a partially qualified Trait Collection if they use one of the creation methods provided by Apple, such as UITraitCollection.FromHorizontalSizeClass, to create a new collection.

可对多个特征集合执行的一项操作是将它们彼此进行比较,这涉及到一个特征集合(如果它包含另一个)。One operation that can be performed on multiple Trait Collections is comparing them to each other, which involves asking one Trait Collection if it contains another one. 包含的含义是,对于第二个集合中指定的任何特征,值必须与第一个集合中的值完全匹配。What is meant by Containment is that, for any trait specified in the second collection, the value must match exactly with the value in the first collection.

若要测试两个特性,请使用 UITraitCollectionContains 方法,并传入要测试的特性的值。To test two traits use the Contains method of the UITraitCollection passing in the value of the trait to be tested.

开发人员可以在代码中手动执行比较,以确定如何布局视图或查看控制器。The developer can perform the comparisons manually in code to determine how to layout Views or View Controllers. 不过,UIKit 在内部使用此方法来提供它的某些功能(例如,在外观代理中)。However, UIKit uses this method internally to provide some of its functionality, as in the Appearance Proxy, for example.

外观代理Appearance Proxy

外观代理是在早期版本的 iOS 中引入的,以允许开发人员自定义视图的属性。The Appearance Proxy was introduced in earlier versions of iOS to allow developers to customize the properties of their Views. 它已在 iOS 8 中扩展以支持特征集合。It has been extended in iOS 8 to support Trait Collections.

外观代理现在包含新方法 AppearanceForTraitCollection,该方法为已传入的给定特征集合返回新的外观代理。Appearance Proxies now include a new method, AppearanceForTraitCollection, that returns a new Appearance Proxy for the given Trait Collection that has been passed in. 开发人员对该外观代理执行的任何自定义仅会对符合指定特征集合的视图生效。Any customizations that the developer performs on that Appearance Proxy will only take effect on Views that conform to the specified Trait Collection.

通常,开发人员会将部分指定的特征集合传递到 AppearanceForTraitCollection 方法,例如刚指定了 Compact 的水平大小的类,以便他们可以在应用程序中自定义横向精简的任何视图。Generally the developer will pass in a partially specified Trait Collection to the AppearanceForTraitCollection method, such as one that just specified a Horizontal Size Class of Compact, so that they could customize any view in the application that is compact horizontally.

UIImageUIImage

Apple 向添加了特征集合的另一个类 UIImageAnother class that Apple has added Trait Collection to is UIImage. 在过去,开发人员必须指定要在应用程序中包括的任何位图图形资产的 @1X 和 @2x 版本(例如图标)。In the past the developer had to specify a @1X and @2x version of any bitmapped graphic asset that they were going to include in the application (such as an icon).

iOS 8 已扩展为允许开发人员在基于特征集合的映像目录中包含多个版本的映像。iOS 8 has been expanded to allow the developer to include multiple version of an image in an Image Catalog based on a Trait Collection. 例如,开发人员可以包含一个较小的图像,用于处理精简特征类和任何其他集合的完全大小的图像。For example, the developer could include a smaller image for working with a Compact Trait Class and a full sized image for any other collection.

当在 UIImageView 类内部使用其中一种图像时,"图像" 视图将自动显示其特征集合的正确版本的图像。When one of the images is used inside of a UIImageView class, the Image View will automatically display the correct version of the image for its Trait Collection. 如果特征环境发生变化(例如,用户将设备从纵向切换为横向),则图像视图将自动选择新图像大小以匹配新的特征集合,并更改其大小以匹配当前版本的图像会.If the Trait Environment changes (such as the user switching the device from portrait to landscape), the Image View will automatically select the new image size to match the new Trait Collection and change its size to match that of the current version of the image being displayed.

UIImageAssetUIImageAsset

Apple 向 iOS 8 添加了一个名为 "UIImageAsset" 的新类,使开发人员可以更好地控制图像选择。Apple has added a new class to iOS 8 called UIImageAsset to give the developer even more control over image selection.

图像资产会包装图像的所有不同版本,并允许开发人员询问与已传入的特征集合匹配的特定图像。An Image Asset wraps up all of the different versions of an image and allows the developer to ask for a specific image that matches a Trait Collection that has been passed in. 可以动态地从图像资产中添加或删除图像。Images can be added or removed from an Image Asset, on-the-fly.

有关图像资产的详细信息,请参阅 Apple 的UIImageAsset文档。For more information on Image Assets, see Apple's UIImageAsset documentation.

组合特征集合Combining Trait Collections

开发人员可以对特征集合执行的另一个函数是添加两个,它们将导致组合集合,其中一个集合中的未指定值将替换为第二个集合中的指定值。Another function that a developer can perform on Trait Collections is to add two together that will result in the combined collection, where the unspecified values from one collection are replaced by the specified values in a second one. 使用 UITraitCollection 类的 FromTraitsFromCollections 方法完成此操作。This is done using the FromTraitsFromCollections method of the UITraitCollection class.

如上所述,如果其中一个特征集合未指定任何特征并且在另一个特征集合中指定,则该值将设置为指定的版本。As stated above, if any of the traits is unspecified in one of the Trait Collections and is specified in another, the value will be set to the specified version. 但是,如果指定了多个版本的给定值,则最后一个特性集合中的值将是使用的值。However, if there are multiple versions of a given value specified, the value from the last Trait Collection will be the value that is used.

自适应视图控制器Adaptive View Controllers

本部分将介绍如何在开发人员的应用程序中将 iOS 视图和视图控制器采用了特征和大小类的概念。This section will cover the details of how the iOS View and View Controllers have adopted the concepts of Traits and Size Classes to automatically be more adaptive in the developer's applications.

拆分视图控制器Split View Controller

IOS 8 中已更改的其中一个视图控制器类是 UISplitViewController 类。One of the View Controller classes that has changed the most in iOS 8 is the UISplitViewController class. 过去,开发人员通常会在 iPad 版应用程序上使用拆分视图控制器,然后他们必须为 iPhone 版本的应用提供完全不同的视图层次结构版本。In the past, the developer would often use a Split View Controller on the iPad version of the application and then they would have to provide a completely different version of their view hierarchy for the iPhone version of the app.

在 iOS 8 中,UISplitViewController 类在平台(iPad 和 iPhone)上可用,这允许开发人员创建一个可同时适用于 iPhone 和 iPad 的视图控制器层次结构。In iOS 8, the UISplitViewController class is available on both platforms (iPad and iPhone), which allows the developer to create one View Controller hierarchy that will function for both iPhone and iPad.

当 iPhone 处于横向时,拆分视图控制器将并排显示其视图,就像在 iPad 上显示一样。When an iPhone is in Landscape, the Split View Controller will present its Views side-by-side, just as it would when being displayed on an iPad.

覆盖特征Overriding Traits

特性环境从父容器级联向下级联到子容器,如下图所示,以横向显示 iPad 上的拆分视图控制器:Trait Environments cascade from the parent container down to the child containers, as in the following graphic showing a Split View Controller on an iPad in the landscape orientation:

由于 iPad 在水平和垂直方向上都有一个常规大小类,因此 "拆分" 视图将显示主视图和详细信息视图。Since the iPad has a Regular Size Class in both the horizontal and vertical orientations, the Split View will display both the master and detail views.

在 iPhone 上,在这两个方向上,"拆分" 视图控制器仅显示详细信息视图,如下所示:On an iPhone, where the Size Class is compact in both orientations, the Split View Controller only displays the detail view, as seen below:

在开发人员想要以横向方向显示 iPhone 上的主视图和详细信息视图的应用程序中,开发人员必须为拆分视图控制器插入父容器,并覆盖特征集合。In an application where the developer wants to display both the master and detail view on an iPhone in the landscape orientation, the developer must insert a parent container for the Split View Controller and override the Trait Collection. 如下图所示:As seen in the graphic below:

UIView 设置为拆分视图控制器的父级,并对在新的特征集合中传递并以拆分视图控制器为目标的视图调用 SetOverrideTraitCollection 方法。A UIView is set as the parent of the Split View Controller and the SetOverrideTraitCollection method is called on the view passing in a new Trait Collection and targeting the Split View Controller. 新的特征集合将覆盖 HorizontalSizeClass,并将其设置为 Regular,以便拆分视图控制器将以横向方向显示 iPhone 上的主视图和详细信息视图。The new Trait Collection overrides the HorizontalSizeClass, setting it to Regular, so that the Split View Controller will display both the master and detail views on an iPhone in the landscape orientation.

请注意,VerticalSizeClass 设置为 unspecified,这允许将新的特性集合添加到父项上的 "特性" 集合中,从而为子拆分视图控制器提供 Compact VerticalSizeClassNote that the VerticalSizeClass was set to unspecified, which allows the new Trait Collection to be added to the Trait Collection on the parent, resulting in a Compact VerticalSizeClass for the child Split View Controller.

特征更改Trait Changes

本部分将详细介绍特征集如何在特征环境改变时进行转换。This section will take a look, in detail, at how the Trait Collections transition when the Trait Environment changes. 例如,将设备从纵向旋转到横向时。For example, when the device is rotated from portrait to landscape.

首先,iOS 8 执行一些设置,以便准备进行过渡。First, iOS 8 does some setup to prepare for the transition to take place. 接下来,系统会对转换状态进行动画处理。Next, the system animates the transition state. 最后,iOS 8 清除在转换过程中所需的任何临时状态。Finally, iOS 8 cleans-up any temporary states required during the transition.

iOS 8 提供若干次回调,开发人员可以使用这些回调来参与特征更改,如下表所示:iOS 8 provides several callbacks that the developer can use to participate in the Trait Change as seen in the following table:

PhasePhase 回调Callback 描述Description
安装Setup
  • WillTransitionToTraitCollection
  • TraitCollectionDidChange
  • 此方法在特性集合设置为其新值之前在特征更改开始时调用。This method gets called at the beginning of a Trait Change before a Trait Collection gets set to its new value.
  • 当特征集合的值已更改,但在发生任何动画之前,将调用方法。The method gets called when the value of the Trait Collection has changed but before any animation takes place.
动画Animation WillTransitionToTraitCollection 传递给此方法的转换协调器具有 AnimateAlongside 属性,使开发人员能够添加将与默认动画一起执行的动画。The Transition Coordinator that gets passed to this method has an AnimateAlongside property that allows the developer to add animations that will be executed along with the default animations.
清理Clean-up WillTransitionToTraitCollection 为开发人员提供一种方法,用于在发生过渡后包含自己的清理代码。Provides a method for developers to include their own cleanup code after the transition takes place.

WillTransitionToTraitCollection 方法非常适合用于对视图控制器进行动画处理,以及特征集合更改。The WillTransitionToTraitCollection method is great for animating View Controllers along with the Trait Collection changes. WillTransitionToTraitCollection 方法仅适用于视图控制器(UIViewController),不能用于其他特性环境(如 UIViews)。The WillTransitionToTraitCollection method is only available on View Controllers ( UIViewController) and not on other Trait Environments, like UIViews.

TraitCollectionDidChange 非常适合用于处理 UIView 类,开发人员希望在改变特征时更新 UI。The TraitCollectionDidChange is great for working with the UIView class, where the developer wants to update the UI as the traits are changing.

折叠拆分视图控制器Collapsing the Split View Controllers

现在,让我们详细了解拆分视图控制器从两列到一个列视图的折叠情况。Now let's take a closer look at what happens when a Split View Controller collapses from a two column to a one column view. 作为此更改的一部分,需要执行两个过程:As part of this change, there are two processes that need to occur:

  • 默认情况下,展开后,拆分视图控制器将使用主视图控制器作为视图。By default, the Split View Controller will use the primary view controller as the view after the collapse occurs. 开发人员可以重写此行为,方法是重写 UISplitViewControllerDelegateGetPrimaryViewControllerForCollapsingSplitViewController 方法,并提供他们想要在折叠状态下显示的任何视图控制器。The developer can override this behavior by overriding the GetPrimaryViewControllerForCollapsingSplitViewController method of the UISplitViewControllerDelegate and providing any View Controller that they want to display in the collapsed state.
  • 辅助视图控制器必须合并到主视图控制器。The Secondary View Controller has to get merged into the Primary View Controller. 通常,开发人员无需在此步骤中执行任何操作;拆分视图控制器包括基于硬件设备的此阶段的自动处理。Generally the developer will not need to take any action for this step; the Split View Controller includes automatic handling of this phase based on the hardware device. 但是,在某些特殊情况下,开发人员需要与此更改进行交互。However, there may be some special cases where the developer will want to interact with this change. 如果调用了 UISplitViewControllerDelegateCollapseSecondViewController 方法,则允许在折叠发生时显示母版视图控制器,而不是详细信息视图。Calling the CollapseSecondViewController method of the UISplitViewControllerDelegate allows the master view controller to be displayed when the collapse occurs, instead of the details view.

展开拆分视图控制器Expanding the Split View Controller

现在,让我们详细了解拆分视图控制器从折叠状态扩展后会发生的情况。Now let's take a closer look at what happens when a Split View Controller is expanded from a collapsed state. 同样,需要执行两个阶段:Once again, there are two stages that need to occur:

  • 首先,定义新的主视图控制器。First, define the new Primary View Controller. 默认情况下,拆分视图控制器将自动从折叠视图使用主视图控制器。By default, the Split View Controller will automatically use the Primary View Controller from the collapsed view. 同样,开发人员可以使用 UISplitViewControllerDelegateGetPrimaryViewControllerForExpandingSplitViewController 方法来重写此行为。Again, the developer can override this behavior using the GetPrimaryViewControllerForExpandingSplitViewController method of the UISplitViewControllerDelegate .
  • 选择主视图控制器后,必须重新创建辅助视图控制器。Once the Primary View Controller has been chosen, the Secondary View Controller must be recreated. 同样,拆分视图控制器包括基于硬件设备的此阶段的自动处理。Again, the Split View Controller includes automatic handling of this phase based on the hardware device. 开发人员可以通过调用 UISplitViewControllerDelegateSeparateSecondaryViewController 方法来重写此行为。The developer can override this behavior by calling the SeparateSecondaryViewController method of the UISplitViewControllerDelegate .

在拆分视图控制器中,主视图控制器通过实现 UISplitViewControllerDelegateCollapseSecondViewControllerSeparateSecondaryViewController 方法,在展开和折叠视图中扮演着一个部分。In a Split View Controller, the Primary View Controller plays a part in both the expanding and collapsing of the views by implementing the CollapseSecondViewController and SeparateSecondaryViewController methods of the UISplitViewControllerDelegate. UINavigationController 实现这些方法以自动推送和弹出辅助视图控制器。UINavigationController implements these methods to automatically push and pop the Secondary View controller.

显示视图控制器Showing View Controllers

Apple 对 iOS 8 进行的另一项更改是开发人员显示视图控制器的方式。Another change that Apple has made to iOS 8 is in the way that the developer shows View Controllers. 过去,如果应用程序有叶视图控制器(如表视图控制器),并且开发人员显示了不同的控制器(例如,为了响应用户点击某个单元),则应用程序将通过控制器层次结构返回到导航视图控制器并对其调用 PushViewController 方法,以显示新的视图。In the past, if the application had a Leaf View Controller (such as a Table View Controller), and the developer showed a different one (for example, in response to the user tapping on a cell), the application would reach back through the controller hierarchy to the Navigation View Controller and call the PushViewController method against it to display the new view.

这会在导航控制器和它运行时所处的环境之间出现紧密耦合。This presented a very tight coupling between the Navigation Controller and the environment that it was running in. 在 iOS 8 中,Apple 通过提供两种新方法来分离此内容:In iOS 8, Apple has decoupled this by providing two new methods:

  • ShowViewController –改编以根据其环境显示新的视图控制器。ShowViewController – Adapts to display the new view controller based on its environment. 例如,在 UINavigationController 它只是将新视图推送到堆栈上。For example, in a UINavigationController it simply pushes the new view onto the stack. 在拆分视图控制器中,新的视图控制器将作为新的主视图控制器显示在左侧。In a Split View Controller, the new View Controller will be presented on the left side as the new Primary View Controller. 如果没有容器视图控制器,则新视图将显示为模式视图控制器。If no container view controller is present, the new view will be displayed as a Modal View Controller.
  • ShowDetailViewController –工作方式类似于 ShowViewController,但在拆分视图控制器上实现,用于将详细信息视图替换为传入的新视图控制器。ShowDetailViewController – Works in a similar fashion to ShowViewController, but is implemented on a Split View Controller to replace the details view with the new View Controller being passed in. 如果拆分视图控制器是折叠的(在 iPhone 应用程序中可能会看到),则调用将被重定向到 ShowViewController 方法,新视图将显示为主视图控制器。If the Split View Controller is collapsed (as might be seen in an iPhone Application), the call will be redirected to the ShowViewController method, and the new view will be shown as the Primary View Controller. 同样,如果不存在容器视图控制器,则新视图将显示为模式视图控制器。Again, if no container view controller is present, the new view will be displayed as a Modal View Controller.

这些方法的工作方式是从叶视图控制器开始,遍历视图层次结构,直到找到正确的容器视图控制器来处理新视图的显示。These methods work by starting at the Leaf View Controller and walk up the view hierarchy until they find the right container view controller to handle the display of the new view.

开发人员可以在自己的自定义视图控制器中实现 ShowViewControllerShowDetailViewController,以获取 UINavigationControllerUISplitViewController 提供的相同自动化功能。Developers can implement ShowViewController and ShowDetailViewController in their own custom View Controllers to get the same automated functionality that UINavigationController and UISplitViewController provides.

工作原理How it works

在本部分中,我们将介绍如何在 iOS 8 中实际实现这些方法。In this section we will take a look at how these methods are actually implemented in iOS 8. 首先,让我们看一看新的 GetTargetForAction 方法:First let’s look at the new GetTargetForAction method:

此方法将遍历层次结构链,直到找到正确的容器视图控制器。This method walks the hierarchy chain until the correct container view controller is found. 例如:For example:

  1. 如果调用 ShowViewController 方法,则实现此方法的链中的第一个视图控制器是导航控制器,因此它将用作新视图的父级。If a ShowViewController method is called, the first View Controller in the chain that implements this method is the Navigation Controller, so it is used as the parent of the new view.
  2. 如果改为调用 ShowDetailViewController 方法,则拆分视图控制器是第一个要实现它的视图控制器,因此它将用作父级。If a ShowDetailViewController method was called instead, the Split View Controller is the first View Controller to implement it, so it is used as the parent.

GetTargetForAction 方法的工作方式是:查找实现给定操作的视图控制器,然后询问视图控制器(如果它要接收该操作)。The GetTargetForAction method works by locating a View Controller that implements a given Action and then asking that View Controller if it wants to receive that action. 由于此方法是公共的,因此开发人员可以创建自己的自定义方法,就像内置 ShowViewControllerShowDetailViewController 方法一样。Since this method is public, developers can create their own custom methods that function just like the built in ShowViewController and ShowDetailViewController methods.

自适应演示Adaptive Presentation

在 iOS 8 中,Apple 已将 Segue 演示文稿(UIPopoverPresentationController)设为自适应。In iOS 8, Apple has made Popover Presentations ( UIPopoverPresentationController) adaptive as well. 因此,Segue 演示视图控制器会自动在常规大小类中显示普通的 Segue 视图,但会在水平紧凑的大小类(如 iPhone)上以全屏方式显示它。So a Popover Presentation View Controller will automatically present a normal Popover View in a Regular Size Class, but will display it full screen in a horizontally compact Size Class (such as on an iPhone).

为了适应统一情节提要系统中的更改,创建了一个新的控制器对象用于管理所提供的视图控制器,UIPresentationControllerTo accommodate the changes within the unified storyboard system, a new controller object has been created to manage the presented View Controllers — UIPresentationController. 此控制器是从显示视图控制器到关闭视图控制器时创建的。This controller is created from the time the View Controller is presented until it is dismissed. 由于它是一个管理类,因此它可以在视图控制器上被视为一个超级类,因为它对影响视图控制器(如方向)的设备更改进行响应,然后将其反馈到显示控制器控件的视图控制器。As it is a managing class, it can be considered a super class over the View Controller as it responds to device changes that affect the View Controller (such as orientation) which are then fed back into the View Controller the Presentation Controller controls.

当开发人员使用 PresentViewController 方法呈现视图控制器时,会将演示过程的管理传递到 UIKit中。When the developer presents a View Controller using the PresentViewController method, the management of the presentation process is handed to UIKit. UIKit 处理要创建的样式的正确控制器,其中唯一的例外是将视图控制器的样式设置为 UIModalPresentationCustomUIKit handles (among other things) the correct controller for the style being created, with the only exception being when a View Controller has the style set to UIModalPresentationCustom. 此时,应用程序可以提供自己的 PresentationController,而不是使用 UIKit 控制器。Here, the application can provide it’s own PresentationController rather than using the UIKit controller.

自定义演示样式Custom Presentation Styles

使用自定义演示样式,开发人员可以选择使用自定义呈现控制器。With a custom presentation style, developers have the option to use a custom Presentation Controller. 此自定义控制器可用于修改 allied 的视图的外观和行为。This custom controller can be used to modify the appearance and behavior of the View it is allied to.

使用大小类Working with Size Classes

本文随附的自适应照片 Xamarin 项目提供了在 iOS 8 统一界面应用程序中使用大小类和自适应视图控制器的工作示例。The Adaptive Photos Xamarin Project that is included with this article gives a working example of using Size Classes and Adaptive View Controllers in an iOS 8 Unified Interface application.

尽管应用程序完全是通过代码创建的 UI,但与使用 IOS 设计器并创建统一的情节提要不同,这种方法同样适用。While the application creates its UI completely from code, as opposed to using the IOS Designer and creating a Unified Storyboard, the same techniques apply. 在本文的后面部分,我们将演示如何在 Xamarin 应用程序中结合使用大小类和统一情节提要和 iOS 设计器。Later in this article, we’ll show how to use Size Classes with a Unified Storyboard and the iOS Designer in a Xamarin Application.

现在,让我们详细了解自适应照片项目如何实现 iOS 8 中的几个大小类功能,以创建自适应应用程序。Now let’s take a closer look at how the Adaptive Photos project is implementing several of the Size Class features in iOS 8 to create a Adaptive Application.

适应特征环境更改Adapting to Trait Environment Changes

当在 iPhone 上运行自适应照片应用程序时,当用户将设备从纵向旋转到横向时,拆分视图控制器将显示大纲视图和详细信息视图:When running the Adaptive Photos application on an iPhone, when the user rotates the device from portrait to landscape, the Split View Controller will display both the master and details view:

这是通过以下方式实现的:重写视图控制器的 UpdateConstraintsForTraitCollection 方法,并基于 VerticalSizeClass的值调整约束。This is accomplished by overriding the UpdateConstraintsForTraitCollection method of the View Controller and adjusting the constraints based on the value of the VerticalSizeClass. 例如:For example:

public void UpdateConstraintsForTraitCollection (UITraitCollection collection)
{
    var views = NSDictionary.FromObjectsAndKeys (
        new object[] { TopLayoutGuide, ImageView, NameLabel, ConversationsLabel, PhotosLabel },
        new object[] { "topLayoutGuide", "imageView", "nameLabel", "conversationsLabel", "photosLabel" }
    );

    var newConstraints = new List<NSLayoutConstraint> ();
    if (collection.VerticalSizeClass == UIUserInterfaceSizeClass.Compact) {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide][imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.Add (NSLayoutConstraint.Create (ImageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal,
            View, NSLayoutAttribute.Width, 0.5f, 0.0f));
    } else {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]-20-[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));
    }

    if (constraints != null)
        View.RemoveConstraints (constraints.ToArray ());

    constraints = newConstraints;
    View.AddConstraints (constraints.ToArray ());
}

添加过渡动画Adding Transition Animations

自适应照片应用程序中的拆分视图控制器从折叠展开为展开时,会通过覆盖视图控制器的 WillTransitionToTraitCollection 方法,将动画添加到默认动画。When the Split View Controller in the Adaptive Photos application goes from collapsed to expanded, animations are added to the default animations by overriding the WillTransitionToTraitCollection method of the view controller. 例如:For example:

public override void WillTransitionToTraitCollection (UITraitCollection traitCollection, IUIViewControllerTransitionCoordinator coordinator)
{
    base.WillTransitionToTraitCollection (traitCollection, coordinator);
    coordinator.AnimateAlongsideTransition ((UIViewControllerTransitionCoordinatorContext) => {
        UpdateConstraintsForTraitCollection (traitCollection);
        View.SetNeedsLayout ();
    }, (UIViewControllerTransitionCoordinatorContext) => {
    });
}

覆盖特征环境Overriding the Trait Environment

如上所示,当 iPhone 设备处于横向视图中时,自适应照片应用程序强制拆分视图控制器显示详细信息和主视图。As show above, the Adaptive Photos application forces the Split View Controller to display both the details and the master views when the iPhone device is in the landscape view.

这是通过在视图控制器中使用以下代码来实现的:This was accomplished by using the following code in the View Controller:

private UITraitCollection forcedTraitCollection = new UITraitCollection ();
...

public UITraitCollection ForcedTraitCollection {
    get {
        return forcedTraitCollection;
    }

    set {
        if (value != forcedTraitCollection) {
            forcedTraitCollection = value;
            UpdateForcedTraitCollection ();
        }
    }
}
...

public override void ViewWillTransitionToSize (SizeF toSize, IUIViewControllerTransitionCoordinator coordinator)
{
    ForcedTraitCollection = toSize.Width > 320.0f ?
         UITraitCollection.FromHorizontalSizeClass (UIUserInterfaceSizeClass.Regular) :
         new UITraitCollection ();

    base.ViewWillTransitionToSize (toSize, coordinator);
}

public void UpdateForcedTraitCollection ()
{
    SetOverrideTraitCollection (forcedTraitCollection, viewController);
}

展开和折叠拆分视图控制器Expanding and Collapsing the Split View Controller

接下来,我们将探讨如何在 Xamarin 中实现拆分视图控制器的展开和折叠行为。Next let’s examine how the expanding and collapsing behavior of the Split View Controller was implemented in Xamarin. AppDelegate中,创建拆分视图控制器后,将分配其委托来处理这些更改:In the AppDelegate, when the Split View Controller is created, its delegate is assigned to handle these changes:

public class SplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool CollapseSecondViewController (UISplitViewController splitViewController,
        UIViewController secondaryViewController, UIViewController primaryViewController)
    {
        AAPLPhoto photo = ((CustomViewController)secondaryViewController).Aapl_containedPhoto (null);
        if (photo == null) {
            return true;
        }

        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            var viewControllers = new List<UIViewController> ();
            foreach (var controller in ((UINavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containsPhoto");

                if ((bool)method.Invoke (controller, new object[] { null })) {
                    viewControllers.Add (controller);
                }
            }

            ((UINavigationController)primaryViewController).ViewControllers = viewControllers.ToArray<UIViewController> ();
        }

        return false;
    }

    public override UIViewController SeparateSecondaryViewController (UISplitViewController splitViewController,
        UIViewController primaryViewController)
    {
        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            foreach (var controller in ((CustomNavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containedPhoto");

                if (method.Invoke (controller, new object[] { null }) != null) {
                    return null;
                }
            }
        }

        return new AAPLEmptyViewController ();
    }
}

SeparateSecondaryViewController 方法测试是否正在显示照片,并根据该状态采取措施。The SeparateSecondaryViewController method tests to see if a photo is being displayed and takes action based on that state. 如果未显示任何照片,它会折叠辅助视图控制器,以便显示母版视图控制器。If no photo is being shown it collapses the Secondary View Controller so that the Master View Controller is displayed.

展开拆分视图控制器以查看堆栈上是否存在任何照片时,将使用 CollapseSecondViewController 方法(如果有的话)。The CollapseSecondViewController method is used when expanding the Split View Controller to see if any photos exist on the stack, if so it collapses back to that view.

在视图控制器之间移动Moving Between View Controllers

接下来,让我们看看自适应照片应用程序如何在视图控制器之间移动。Next, let’s take a look at how the Adaptive Photos application moves between view controllers. 当用户从表中选择某个单元格时,在 AAPLConversationViewController 类中,将调用 ShowDetailViewController 方法来显示详细信息视图:In the AAPLConversationViewController class when the user selects a cell from the table, the ShowDetailViewController method is called to display the details view:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    var photo = PhotoForIndexPath (indexPath);
    var controller = new AAPLPhotoViewController ();
    controller.Photo = photo;

    int photoNumber = indexPath.Row + 1;
    int photoCount = (int)Conversation.Photos.Count;
    controller.Title = string.Format ("{0} of {1}", photoNumber, photoCount);
    ShowDetailViewController (controller, this);
}

显示泄露指示器Displaying Disclosure Indicators

在自适应照片应用程序中,有几个位置会根据特征环境的变化隐藏或显示泄露指示器。In the Adaptive Photo application there are several places where Disclosure Indicators are hidden or shown based on changes to the Trait Environment. 此操作通过以下代码进行处理:This is handled with the following code:

public bool Aapl_willShowingViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

public bool Aapl_willShowingDetailViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingDetailViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

这些使用以上详细讨论的 GetTargetViewControllerForAction 方法实现。These are implemented using the GetTargetViewControllerForAction method discussed in detail above.

当表视图控制器正在显示数据时,它将使用上述方法来查看是否将发生推送,以及是否相应地显示或隐藏泄露指示器:When a Table View Controller is displaying data, it uses the methods implemented above to see whether or not a push is going to happen, and whether or not to display or hide the Disclosure Indicator accordingly:

public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
    bool pushes = ShouldShowConversationViewForIndexPath (indexPath) ?
         Aapl_willShowingViewControllerPushWithSender () :
         Aapl_willShowingDetailViewControllerPushWithSender ();

    cell.Accessory = pushes ? UITableViewCellAccessory.DisclosureIndicator : UITableViewCellAccessory.None;
    var conversation = ConversationForIndexPath (indexPath);
    cell.TextLabel.Text = conversation.Name;
}

新建 ShowDetailTargetDidChangeNotification 类型New ShowDetailTargetDidChangeNotification Type

Apple 添加了一个新的通知类型,用于从拆分视图控制器内使用大小类和特征环境,ShowDetailTargetDidChangeNotificationApple has added a new notification type for working with Size Classes and Trait Environments from within a Split View Controller, ShowDetailTargetDidChangeNotification. 只要拆分视图控制器的目标详细信息视图发生更改(如控制器展开或折叠时),就会发送此通知。This notification gets sent whenever the target Detail View for a Split View Controller changes, such as when the controller expands or collapses.

自适应照片应用程序使用此通知在详细信息视图控制器发生更改时更新泄露指示器的状态:The Adaptive Photos application uses this notification to update the state of the Disclosure Indicator when the Detail View Controller changes:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    TableView.RegisterClassForCellReuse (typeof(UITableViewCell), AAPLListTableViewControllerCellIdentifier);
    NSNotificationCenter.DefaultCenter.AddObserver (this, new Selector ("showDetailTargetDidChange:"),
        UIViewController.ShowDetailTargetDidChangeNotification, null);
    ClearsSelectionOnViewWillAppear = false;
}

详细了解自适应照片应用程序,以查看大小类、特征集合和自适应视图控制器可用于在 Xamarin 中轻松创建统一的应用程序的所有方法。Take a closer look at the Adaptive Photos application to see all of the ways that Size Classes, Trait Collections and Adaptive View Controllers can be used to easily create a Unified Application in Xamarin.iOS.

统一的情节提要Unified Storyboards

使用 iOS 8 的新手,统一情节提要允许开发人员创建一个统一的情节提要文件,该文件可通过面向多个大小类在 iPhone 和 iPad 设备上显示。New to iOS 8, Unified Storyboards allow the developer to create one, unified storyboard file that can be displayed on both iPhone and iPad devices by targeting multiple Size Classes. 通过使用统一的情节提要,开发人员可编写更少的 UI 特定的代码,并只提供一个接口设计来创建和维护。By using Unified Storyboards, the developer writes less UI specific code and has only one interface design to create and maintain.

统一情节提要的主要优点是:The key benefits of Unified Storyboards are:

  • 使用适用于 iPhone 和 iPad 的相同情节提要文件。Use the same storyboard file for iPhone and iPad.
  • 向后部署到 iOS 6 和 iOS 7。Deploy backwards to iOS 6 and iOS 7.
  • 在 Xamarin iOS 设计器中预览不同设备、方向和操作系统版本的布局。Preview the layout for different devices, orientations and OS versions all from within the Xamarin iOS Designer.

此功能在 Visual Studio for Mac 完全受支持This feature is fully supported in Visual Studio for Mac

启用大小类Enabling Size Classes

默认情况下,任何新的 Xamarin iOS 项目都将使用大小类。By default, any new Xamarin.iOS project will us size classes. 若要在更早项目的情节提要内使用大小类和自适应 Segue,必须先将其从 iOS 设计器内部转换为 Xcode 6 统一情节提要格式。To use Size Classes and Adaptive Segues inside a storyboard from an older project, it must first be converted to the Xcode 6 Unified Storyboard format from inside the iOS Designer.

为此,请在 iOS 设计器中打开要转换的情节提要,并选中 "使用大小类" 复选框:To do this open the Storyboard to be converted in the iOS Designer and check the Use Size Classes check box:

IOS 设计器将确认开发人员想要将情节提要的格式转换为使用大小类:The iOS Designer will confirm that the developer wants to convert the format of the storyboard to use Size Classes:

重要

还必须检查自动布局的大小类才能正常工作。Auto Layout must also be checked for Size Classes to work correctly.

泛型设备类型Generic Device Types

将情节提要转换为使用大小类后,它将在 Design Surface 中重新显示,并且作为设备的视图将为泛型:Once the storyboard has been converted to use Size Classes, it will be redisplayed in the Design Surface and the View As device will be Generic:

选择 "通用设备类型" 后,所有视图控制器都将调整为 600 x 600 正方形。When the Generic device type is selected, all View Controllers will be resized to a 600 x 600 Square. 此正方形表示任何宽度和高度的大小。This square represents sizes of any width and any height. 当 iOS 设计器处于此模式下时,任何编辑都将应用于所有大小类。When the iOS Designer is in this mode, any edits will apply to all of the Size Classes.

开发人员还可以选择查看作为 iPhone 的设计图面:The developer also has the option of viewing the design surface as an iPhone:

或者将其查看为 iPad:Or viewing it as an iPad:

选择大小类Select a Size Class

Size 类选择器按钮位于 Design Surface 的左上角(位于视图附近)。The Size Class Selector button is at the upper left hand corner of the Design Surface (near the View As dropdown). 它允许开发人员选择当前正在编辑的大小类:It allows the developer to select which Size Classes are currently being edited:

选择器将显示大小类选择作为 3 x 3 网格。The selector presents the Size Class selection as a 3 x 3 grid. 网格中的每个方形都表示 Width 类和 Height 类的组合。Each of the squares in the grid represents a combination of a Width Class and a Height Class. 中心方块选择任意宽度/任意高度大小类(统一情节提要的默认视图)。The center square selects the Any Width/Any Height Size class (which is the default view for a Unified Storyboard). 选中此方形后,开发人员将编辑默认布局,此布局由所有其他配置继承。When this square is selected, the developer is editing the default layout, which is inherited by all the other configurations.

网格的左上角的方块表示紧凑型宽度/压缩高度大小类:The square in the upper left hand corner of the grid represents the Compact Width/Compact Height Size Class:

此模式对应于处于横向方向的 iPhone。This mode corresponds to an iPhone in the landscape orientation. 网格右下角的方形表示常规宽度/常规高度大小类,表示 iPad:The square in the lower right hand corner of the grid represents the Regular Width/Regular Height Size Class, which represents an iPad:

若要为 iPhone 编辑纵向布局,请选择左下角的方块。To edit the layout for an iPhone in the portrait orientation, select the square in the lower left hand corner. 这表示紧凑型宽度/常规高度大小类:This represents the Compact Width/Regular Height Size Class:

在正方形中单击以将其选中,Design Surface 会更改视图控制器的大小以匹配新的所选内容:Click in the square to select it and the Design Surface will change the size of the View Controllers to match the new selection:

请参阅本文的 Size Class 部分,了解有关大小类及其如何影响 Iphone 和 Ipad 布局的详细信息。See the Size Class section of this article for more information on Size Classes and how they affect layout for iPhones and iPads.

自适应 Segue 类型Adaptive Segue Types

如果开发人员以前使用过情节提要,则他们将熟悉现有的 segue、 模式segue类型。If the developer has used storyboards before, then they will be familiar with the existing segue types of Push, Modal and Popover. 在统一情节提要文件上启用大小类时,将提供以下自适应 Segue 类型(与上述新视图控制器 API 对应的类型):显示显示详细信息When Size Classes are enabled on a Unified Storyboard file, the following Adaptive Segue Types (that correspond to the new View Controller API discussed above) are made available: Show and Show Detail.

重要

启用大小类后,任何现有的 segue 都将转换为新类型。When Size Classes are enabled, any existing segues will be converted to the new types.

采用一个使用统一情节提要的 iOS 8 应用程序示例,该应用程序在母版视图中具有一个简单的游戏导航菜单。Take the example of an iOS 8 Application that uses a Unified Storyboard with a Split View Controller that has a simple game navigation menu in the Master View. 如果用户单击菜单按钮,则在 iPad 上运行时,所选项目的视图控制器应显示在拆分视图控制器的 "详细信息" 部分中。If the user clicks on a menu button, the selected item’s View Controller should be shown in the Details section of the Split View Controller when running on an iPad. 在 iPhone 上,该项的视图控制器应推送到导航堆栈上。On an iPhone the item’s View Controller should be pushed onto the Navigation stack.

若要实现此效果,请在 iOS 设计器控件中单击按钮,并将线条拖到要显示的视图控制器。To achieve this effect, in the iOS Designer control-click on the button and drag a line to the View Controller to be displayed. 释放鼠标按钮后,从 Segue 类型弹出菜单中选择 Show DetailWhen the mouse button is released, select Show Detail from the Segue Type Popup menu:

新的 segue 将在按钮和视图控制器之间创建。The new segue will be created between the button and the View Controller. 现在,在 iPhone 模拟器中运行该应用程序,将显示主菜单:Now run the application in the iPhone Simulator and the Main Menu will be displayed:

单击 "选择游戏" 按钮,项的视图控制器将被推送到导航堆栈:Click on the Select Game button and the item’s View Controller will be pushed onto the Navigation Stack:

停止 iPhone 模拟器并在 iPad 模拟器中运行该应用程序。Stop the iPhone Simulator and run the Application in the iPad Simulator. 切换到横向方向,并再次显示主菜单:Switch to the landscape orientation and the main menu is again displayed:

同样,单击 "选择游戏" 按钮,该项目的 "视图控制器" 将显示在拆分视图控制器的 "详细信息" 部分中:Again, click on the Select Game button and the item’s View Controller is shown in the Details section of the Split View Controller:

从大小类排除元素Excluding an Element from a Size Class

有时,特定大小类中不需要给定元素(如视图、控件或约束)。There are times when a given element (such as a View, Control or a Constraint) is not required inside of a specific Size Class. 若要从大小类中排除元素,请选择要在Design Surface中排除的所需项。To exclude an element from a Size Class, select the desired item to exclude in the Design Surface. 滚动到属性资源管理器的底部,然后单击 "齿轮" 下拉菜单。Scroll to the bottom of the Property Explorer and click the Gear Dropdown menu. 选择要从中排除项的宽度高度的组合:Select the combination of Width and Height to exclude the item from:

新的排除事例将添加到属性资源管理器底部的元素中。A new Exclusion Case will be added to the element in the bottom of the Property Explorer. 接下来,取消选中给定大小类的已安装复选框:Next, uncheck the Installed checkbox for the given Size Class:

将 Design Surface 切换为从中排除项的宽度和高度,并从给定的大小类(而不是整个 UI 设计)中删除它:Switch the Design Surface to the Width and Height that the item was excluded from, it has been removed from the given Size Class, but not the entire UI design:

切换回任意宽度/任意高度大小类,并且该元素仍就地存在:Switching back to the Any Width/Any Height size class and the element is still in place:

当应用程序在 iPad 模拟器中运行时,将显示元素:When the application is run in the iPad Simulator, the element appears:

当应用程序在 iPhone 模拟器上运行时,缺少元素:And when the application is run on the iPhone Simulator, the element is missing:

若要从元素中删除排除事例,只需在 " Design Surface中选择元素,滚动到"属性资源管理器"底部,然后单击要删除的事例旁边的" - "按钮。To remove an Exclusion Case from an element, simply select the element in the Design Surface, scroll to the bottom of the Property Explorer and click the - button beside the case to remove.

若要查看统一情节提要的实现,请查看附加到本文档 UnifiedStoryboard 示例 Xamarin iOS 8 应用程序。To see an implementation of Unified Storyboards, look at the UnifiedStoryboard sample Xamarin iOS 8 application attached to this document.

动态启动屏幕Dynamic Launch Screens

启动屏幕文件在启动 iOS 应用程序时显示为初始屏幕,以便向用户提供对应用实际启动的反馈。The launch screen file is displayed as a splash screen while an iOS application is launching to provide feedback to the user that the app is actually starting-up. 在 iOS 8 之前,开发人员必须为应用程序运行的每种设备类型、方向和屏幕分辨率包含多个 Default.png 的图像资产。Prior to iOS 8, the developer would have to include multiple Default.png image assets for each device type, orientation and screen resolution that the application would be running on. 例如,Default@2x.pngDefault-Landscape@2x~ipad.pngDefault-Portrait@2x~ipad.png等。For example, Default@2x.png, Default-Landscape@2x~ipad.png, Default-Portrait@2x~ipad.png, etc.

这种 Default.png 方法在新的 iPhone 6 和 iPhone 6 Plus 设备(以及即将发布的 Apple Watch)中与所有现有 iPhone 和 iPad 设备进行因式分解,这表示必须已创建并维护。Factoring in the new iPhone 6 and iPhone 6 Plus devices (and the upcoming Apple Watch) with all the existing iPhone and iPad devices, this represents a large array of varying sizes, orientations and resolutions of Default.png startup screen image assets that must be created and maintained. 此外,这些文件可能会很大,并且会 "膨胀" 可交付结果应用程序捆绑,增加从 iTunes 应用商店下载应用程序所需的时间(可能是因为它无法通过移动电话网络传递)并增加最终用户设备上所需的存储量。In addition, these files can be quite large and will "bloat" the deliverable application bundle, increasing the amount of time required to download the application from the iTunes App Store (possibly keeping it from being able to be delivered over a cellular network) and increasing the amount of storage required on the end user's device.

对于 iOS 8,开发人员可在 Xcode 中创建单个原子 .xib 文件,该文件使用自动布局和大小类来创建可用于每个设备、分辨率和方向的动态启动屏幕New to iOS 8, the developer can create a single, atomic .xib file in Xcode that uses Auto Layout and Size Classes to create a Dynamic Launch Screen that will work for every device, resolution and orientation. 这不仅减少了开发人员创建和维护所有必需的图像资产所需的工作量,而且大大减少了应用程序的已安装捆绑的大小。This not only reduces the amount of work required of the developer to create and maintain all the required image assets, but it greatly reduces the size of the application's installed bundle.

动态启动屏幕具有以下限制和注意事项:Dynamic Launch Screens have the following limitations and considerations:

  • 仅使用 UIKit 类。Use only UIKit classes.
  • 使用 UIViewUIViewController 对象的单个根视图。Use a single root view that is a UIView or UIViewController object.
  • 不要对应用程序的代码进行任何连接(不要添加操作插座)。Don’t make any connections to the application's code (don’t add Actions or Outlets).
  • 不要添加 UIWebView 对象。Don’t add UIWebView objects.
  • 不要使用任何自定义类。Don’t use any custom classes.
  • 不要使用运行时属性。Don’t use runtime attributes.

考虑到上述准则,我们来看看如何向现有 Xamarin iOS 8 项目添加动态启动屏幕。With the above guidelines in mind, let's look at adding a Dynamic Launch Screen to an existing Xamarin iOS 8 project.

请执行以下操作:Do the following:

  1. 打开Visual Studio for Mac并加载解决方案,以向添加动态启动屏幕。Open Visual Studio for Mac and load the Solution to add the Dynamic Launch Screen to.

  2. 解决方案资源管理器中,右键单击 MainStoryboard.storyboard 文件,然后选择 "打开方式 > Xcode Interface BuilderIn the Solution Explorer, right-click the MainStoryboard.storyboard file and select Open With > Xcode Interface Builder:

  3. 在 Xcode 中,选择 "文件 > 新建 > 文件 ... ":In Xcode, select File > New > File...:

  4. 选择 " iOS > 用户界面" > 启动屏幕,然后单击 "下一步" 按钮:Select iOS > User Interface > Launch Screen and click the Next button:

  5. 将该文件命名 LaunchScreen.xib 然后单击 "创建" 按钮:Name the file LaunchScreen.xib and click the Create button:

  6. 通过添加图形元素并使用布局约束来为给定设备、方向和屏幕大小定位它们,编辑启动屏幕的设计:Edit the design of the launch screen by adding graphic elements and using Layout Constraints to position them for the given devices, orientations and screen sizes:

  7. 保存对 LaunchScreen.xib所做的更改。Save the changes to LaunchScreen.xib.

  8. 选择应用程序目标和 "常规" 选项卡:Select the Applications Target and the General tab:

  9. 单击 "选择 info.plist " 按钮,选择 Xamarin 应用的 Info.plist,然后单击 "选择" 按钮:Click the Choose Info.plist button, select the Info.plist for the Xamarin app and click the Choose button:

  10. 在 "应用程序图标和启动图像" 部分中,打开 "启动屏幕文件" 下拉列表,并选择上面创建的 LaunchScreen.xibIn the App Icons and Launch Images section, open the Launch Screen File dropdown and choose the LaunchScreen.xib created above:

  11. 保存对文件所做的更改,并返回 Visual Studio for Mac。Save the changes to the file and return to Visual Studio for Mac.

  12. 等待 Visual Studio for Mac 完成与 Xcode 同步更改。Wait for Visual Studio for Mac to finish syncing changes with Xcode.

  13. 解决方案资源管理器中,右键单击资源文件夹,然后选择 "添加 > 添加文件 ... ":In the Solution Explorer, right-click on the Resource folder and select Add > Add Files...:

  14. 选择上面创建的 LaunchScreen.xib 文件,并单击 "打开" 按钮:Select the LaunchScreen.xib file created above and click the Open button:

  15. 生成应用程序。Build the application.

测试动态启动屏幕Testing the Dynamic Launch Screen

在 Visual Studio for Mac 中,选择 iPhone 4 Retina 模拟器并运行该应用程序。In Visual Studio for Mac, select the iPhone 4 Retina simulator and run the application. 动态启动屏幕将以正确的格式和方向显示:The Dynamic Launch Screen will be displayed in the correct format and orientation:

在 Visual Studio for Mac 中停止应用程序,并选择 iPad iOS 8 设备。Stop the application in Visual Studio for Mac and select an iPad iOS 8 device. 运行应用程序,并为此设备和方向正确设置启动屏幕的格式:Run the application and the launch screen will be correctly formatted for this device and orientation:

返回 Visual Studio for Mac,并使应用程序停止运行。Return to Visual Studio for Mac and stop the application from running.

使用 iOS 7Working with iOS 7

若要保持与 iOS 7 的向后兼容性,只需在 iOS 8 应用程序中包括普通 Default.png 映像资产。To maintain backward compatibility with iOS 7, just include the usual Default.png image assets as normal in the iOS 8 application. iOS 将恢复到以前的行为,并在 iOS 7 设备上运行时将这些文件用作启动屏幕。iOS will return to the previous behavior and use those files as the startup screen when running on an iOS 7 device.

若要在 Xamarin 中查看动态启动屏幕的实现,请查看附加到此文档的动态启动屏幕示例 iOS 8 应用程序。To see an implementation of a Dynamic Launch Screen in Xamarin, look at the Dynamic Launch Screens sample iOS 8 application attached to this document.

总结Summary

本文快速查看大小类及其在 iPhone 和 iPad 设备中的影响布局的方式。This article took a quick look at Size Classes and how they affect layout in iPhone and iPad devices. 它讨论了特征、特征环境和特征集如何与大小类结合使用,以创建统一的界面。It discussed how Traits, Trait Environments and Trait Collections work with Size Classes to create Unified Interfaces. 简单了解自适应视图控制器,以及它们如何使用统一接口内的大小类。It took brief look at Adaptive View Controllers and how they work with Size Classes inside of Unified Interfaces. 其中介绍了如何从C# Xamarin iOS 8 应用程序内的代码完全实现大小类和统一接口。It looked at implementing Size Classes and Unified Interfaces completely from C# code inside a Xamarin iOS 8 application.

最后,本文介绍了使用 Xamarin iOS 设计器创建统一情节提要的基础知识,该设计器可跨 iOS 设备工作,并创建单个动态启动屏幕,每个 iOS 8 设备上都将显示为启动屏幕。Finally, this article covered the basics of creating Unified Storyboards with the Xamarin iOS Designer that will work across iOS devices and creating a single, Dynamic Launch Screen that will be displayed as the startup screen on every iOS 8 device.