View Controller Programming Guide for iOS---(三)---Using View Controllers in Your App

时间:2020-12-19 01:35:15

Using View Controllers in Your App

Whether you are working with view controllers provided by iOS, or with custom controllers you’ve created to show your app’s content, you use a similar set of techniques to actually work with the view controllers.

不管你是使用iOS提供的视图控制器,或是使用你创建的用来显示应用程序内容的自定义控制器,使用视图控制器的技术都是相似的。

The most common technique for working with view controllers is to place them inside a storyboard. Placing view controllers in a storyboard allows you to directly establish relationships between the view controllers in your app without writing code. You can see the flow of control—from controllers created when your app first launches, to controllers that are instantiated in response to a user’s actions. iOS manages most of this process by instantiating these view controllers only when the app needs them.

使用视图控制器最常用的技术是把它们放入一个故事板中。把视图控制器放在故事板中让你直接建立视图控制器之间的关系,而不需要编写任何代码。 当应用程序第一次启动时,你可以查看控制流---从创建的控制器开始,到被实例化以响应用户的动作的控制器。iOS通过在应用程序需要时实例化这些视图控制器来管理大部分该过程。

Sometimes you may need to create a view controller by allocating and initializing it programmatically. When working with view controllers directly, you must write code that instantiates the view controller, configures it, and displays it.

有时你可能需要通过程序进行分配和初始化来创建一个视图控制器。 当你直接使用视图控制器时,你必须编写实例化视图控制器的代码,配置它并显示它。

Working with View Controllers in Storyboards

一、在故事板中使用视图控制器

Figure 2-1 shows an example of a storyboard. This storyboard includes view controllers, associated views, and connection arrows that establish relationships between the view controllers. In effect, this storyboard tells a story, starting with one scene and later showing others in response to a user’s actions.

图2-1 显示了一个故事板例子。 该故事板包含了视图控制器, 相关视图,以及在视图控制器之间建立关系的连接箭头。实际上,该故事板讲述了一个故事,以一个场景开始,然后显示其它场景以响应用户动作。

Figure 2-1  A storyboard holds a set of view controllers and associated objectsView Controller Programming Guide for iOS---(三)---Using View Controllers in Your App

A storyboard may designate one view controller to be the initial view controller. If a storyboard represents a specific workflow through part of your UI, the initial view controller represents the first scene in that workflow.

一个故事板可能指定一个视图控制器为初始视图控制器。如果一个故事板代表一个贯穿你的用户界面部分的指定工作流,初始视图控制器表示在那个工作流中的第一个场景。

You establish relationships from the initial view controller to other view controllers in the storyboard. In turn, you establish relationships from those view controllers to others, eventually connecting most or all of the storyboard’s scenes into a single connected graph. The type of relationship you establish determines when a connected view controller is instantiated by iOS, as follows:

你在故事板中为从初始视图控制器到其它视图控制器之间建立关系。然后,你建立从那些视图控制器到其它视图控制器之间的关系,最终连接大多数或全部故事板中的场景,组成一个唯一的连接图。 你建立的关系类型在iOS实例化一个被连接的视图控制器时决定,请看:

  • If the relationship is a segue, the destination view controller is instantiated when the segue is triggered.

    如果是一个segue关系,目标视图控制器在segue被触发时实例化。

  • If the relationship represents containment, the child view controller is instantiated when its parent is instantiated.

    如果是包含关系,子视图控制器在其父视图控制器被实例化时一起被实例化。

  • If the controller is not the destination or child of another controller, it is never instantiated automatically. You must instantiate it from the storyboard programmatically.

    如果控制器不是目标或另一个控制器的子控制器,它绝不会自动被实例化。你必须通过程序从故事板把它实例化。

To identify a specific view controller or segue inside a storyboard, use Interface Builder to assign it an identifier string that uniquely identifies it. To programmatically load a view controller from the storyboard, you must assign it an identifier. Similarly, to trigger a segue programmatically, it must also be assigned an identifier. When a segue is triggered, that segue's identifier is passed to the source view controller, which uses it to determine which segue was triggered. For this reason, consider labeling every segue with an identifier.

要想识别一个特定的视图控制器或一个故事板中的segue, 使用界面生成器给它分配一个标识字符串(identifier string)用来唯一识别它。要想通过程序从故事板加载一个视图控制器,你必须给它分配一个标识符。 相似地,要想通过程序触发一个segue,它也必须被分配一个标识符。当一个segue被触发时,那个segue的标识符被传递给源视图控制器,它被用来决定该触发哪个segue。 因此,考虑给每个segue分配一个标识符。

When you build an app using storyboards, you can use a single storyboard to hold all of its view controllers, or you can create multiple storyboards and implement a portion of the user interface in each. One storyboard in your app is almost always designated as the main storyboard. If there is a main storyboard, iOS loads it automatically; other storyboards must be explicitly loaded by your app.

当你使用故事板建立一个应用程序时,你可以使用一个单一故事板来装载它所有的视图控制器,或者你可以创建多个故事板并在每个故事板里实现用户界面的一部分。应用程序中一个故事板几乎总是被指定为主故事板。如果有一个主故事板,iOS自动加载它;其它故事板必须被应用程序明确地加载。

The Main Storyboard Initializes Your App’s User Interface

1、主故事板初始化应用程序用户界面

The main storyboard is defined in the app’s Information property list file. If a main storyboard is declared in this file, then when your app launches, iOS performs the following steps:

主故事板被定义在应用程序的信息属性列表文件中。如果一个主故事板在该文件中被声明,然后当你的应用程序启动后,iOS执行以下步骤:

  1. It instantiates a window for you.

    它为你实例化一个窗口。

  2. It loads the main storyboard and instantiates its initial view controller.

    它加载主故事板并实例化其初始视图控制器。

  3. It assigns the new view controller to the window’s rootViewController property and then makes the window visible on the screen.

    它把新的视图控制器分配给窗口的rootViewController 属性,然后让窗口在屏幕上可见。

Your app delegate is called to configure the initial view controller before it is displayed. The precise set of steps iOS uses to load the main storyboard is described in “Coordinating Efforts Between View Controllers.”

在初始视图控制器显示之前,它掉用应用程序委托来对它进行配置。iOS加载主故事板的明确步骤在“Coordinating Efforts Between View Controllers.” 中描述。

Segues Automatically Instantiate the Destination View Controller

2、Segues自动实例化目标视图控制器

A segue represents a triggered transition that brings a new view controller into your app’s user interface.

一个segue表示一个触发过渡,它把一个新视图控制器带入你的应用程序用户界面。

Segues contain a lot of information about the transition, including the following:

Segues包含很多关于过渡的信息,包括:

  • The object that caused the segue to be triggered, known as the sender

    触发segue的对象, 它称为sender

  • The source view controller that starts the segue

    开启segue的源视图控制器

  • The destination view controller to be instantiated

    要被实例化的目标视图控制器

  • The kind of transition that should be used to bring the destination view controller onscreen

    过渡的类型,它用来把目标视图控制器带入屏幕。

  • An optional identifier string that identifies that specific segue in the storyboard

    一个可选的标识符字符串,它用来在故事板中识别特定的segue

When a segue is triggered, iOS takes the following actions:

当一个segue被触发,iOS采取了以下动作:

  1. It instantiates the destination view controller using the attribute values you provided in the storyboard.

    它使用你在故事板中提供的属性来实例化目标视图控制器。

  2. It gives the source view controller an opportunity to configure the new controller.

    它给源视图控制器一个机会来配置新控制器。

  3. It performs the transition configured in the segue.

    它执行配置在segue中的过渡。

Note: When you implement custom view controllers, each destination view controller declares public properties and methods used by the source view controller to configure its behavior. In return, your custom source view controllers override storyboard methods provided by the base class to configure the destination view controller. The precise details are in “Coordinating Efforts Between View Controllers.”

注意:当你实现自定义视图控制器时,每个目标视图控制器声明源视图控制器使用的公共属性和方法,以用来配置其行为。相应地,你的自定义源视图控制器重载由基类提供的故事板方法来配置目标视图控制器。具体详情,参见“Coordinating Efforts Between View Controllers.”

Triggering a Segue Programmatically

1) 通过程序触发一个Segue

A segue is usually triggered because an object associated with the source view controller, such as a control or gesture recognizer, triggered the segue. However, a segue can also be triggered programmatically by your app, as long as the segue has an assigned identifier. For example, if you are implementing a game, you might trigger a segue when a match ends. The destination view controller then displays the match’s final scores.

一个segue通常因为跟源视图控制器相关的对象而被触发,一个控件或手势辨认者。然而,一个segue还可以通过程序在应用程序中被触发,只要它有一个分配的标识符。 比如,如果你正在实现一个游戏,你可能在比晒结束时触发一个segue。然后目标视图控制器显示比赛的最终分数。

You programmatically trigger the segue by calling the source view controller’sperformSegueWithIdentifier:sender: method, passing in the identifier for the segue to be triggered. You also pass in another object that acts as the sender. When the source controller is called to configure the destination view controller, both the sender object and the identifier for the segue are provided to it.

你可以通过调用源视图控制器的performSegueWithIdentifier:sender: 方法来程序性触发segue,给要被触发的segue传入标识符。你还需要传入另一个对象作为sender。当源控制器被调用来配置目标视图控制器时,segue的sender对象和标识符都提供给源控制器。

Listing 2-1 shows a simple method that triggers a segue. This example is a portion of a larger example described in“Creating an Alternate Landscape Interface.” In this abbreviated form, you can see that the view controller is receiving an orientation notification. When the view controller is in portrait mode and the device is rotated into landscape orientation, the method uses a segue to present a different view controller onscreen. Because the notification object in this case provides no useful information for performing the segue command, the view controller makes itself the sender.

列表2-1 显示了一个触发一个segue的简单方法。 该例子 是 “Creating an Alternate Landscape Interface.” 的一部分。在这一小段中,你可以看到视图控制器正在接收一个方向(orientation)通知。当视图控制器处于竖直模式时,设备被转向水平方向,该方法使用一个segue来把一个不同的视图控制器呈现到屏幕上。因为本例中的通知对象不给执行该segue命令提供有用的信息,所以视图控制器让自己成为了sender.

Listing 2-1  Triggering a segue programmatically

- (void)orientationChanged:(NSNotification *)notification
{
    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
    if (UIDeviceOrientationIsLandscape(deviceOrientation) &&
        !isShowingLandscapeView)
    {
        [self performSegueWithIdentifier:@"DisplayAlternateView" sender:self];
        isShowingLandscapeView = YES;
    }
// Remainder of example omitted.
}

If a segue can be triggered only programmatically, you usually draw the connection arrow directly from the source view controller to the destination view controller.

如果一个segue只可以通过程序触发,你通常直接从源视图控制器画连接箭头到目标视图控制器。

Instantiating a Storyboard’s View Controller Programmatically

3、通过程序实例化一个故事板的视图控制器

You may want to programmatically instantiate a view controller without using a segue. A storyboard is still valuable, because you can use it to configure the attributes of the view controller as well as its view hierarchy. However, if you do instantiate a view controller programmatically, you do not get any of the behavior of a segue. To display the view controller, you must implement additional code. For this reason, you should rely on segues where possible and use this technique only when needed.

你可能想要通过程序实例化一个视图控制器而不使用一个segue。故事板任然很有价值,因为你可以使用它来配置视图控制器的属性以及它的视图层次结构。 然而,如果你确实通过程序实例化一个视图控制器,你得不到任何关于一个segue的行为。 要想显示视图控制器,你必须实现额外的代码。因为这个原因,在可能的情况下你应该依赖segues并在需要的时候才使用该技术。

Here are the steps your code needs to implement:

以下是你的代码需要实现的步骤:

  1. Obtain a storyboard object (an object of the UIStoryboard class).

    获取一个故事板对象(UIStoryboard 类的一个对象)

    If you have an existing view controller instantiated from the same storyboard, read its storyboard property to retrieve the storyboard. To load a different storyboard, call the UIStoryboard class’sstoryboardWithName:bundle: class method, passing in the name of the storyboard file and an optional bundle parameter.

    如果你有一个已经存在的视图控制器,它从同一个故事板中实例化,读取它的storyboard 属性来取回故事板。 要想加载一个不同的故事板,调用UIStoryboard  类的 storyboardWithName:bundle:类方法,传入故事板文件的名称和一个可选的束参数。

  2. Call the storyboard object’s instantiateViewControllerWithIdentifier: method, passing in the identifier you defined for the view controller when you created it in Interface Builder.

    调用故事板对象的instantiateViewControllerWithIdentifier: 方法,传入你在界面生成器中创建视图控制器时你给它的标识符。

    Alternatively, you can use the instantiateInitialViewController method to instantiate the initial view controller in a storyboard, without needing to know its identifier.

    或者,你可以使用instantiateInitialViewController 方法来在实例化在故事板中的初始视图控制器,不需要知道它的标识符。

  3. Configure the new view controller by setting its properties.

    设置新视图控制器的属性来配置它。

  4. Display the new view controller. See “Displaying a View Controller’s Contents Programmatically.”

    显示新的视图控制器。参见 “Displaying a View Controller’s Contents Programmatically.”

Listing 2-2 shows an example of this technique. It retrieves the storyboard from an existing view controller and instantiates a new view controller using it.

列表2-2 显示了该技术的一个例子。 它从一个已经存在的视图控制器取回故事板并用它实例化一个新的视图控制器。

Listing 2-2  Instantiating another view controller inside the same storyboard

- (IBAction)presentSpecialViewController:(id)sender {
    UIStoryboard *storyboard = self.storyboard;
    SpecialViewController *svc = [storyboard instantiateViewControllerWithIdentifier:@"SpecialViewController"];
 
    // Configure the new view controller here.
 
    [self presentViewController:svc animated:YES completion:nil];
}

Listing 2-3 shows another frequently used technique. This example loads a new storyboard and instantiates its initial view controller. It uses this view controller as the root view controller for a new window being placed on an external screen. To display the returned window, your app calls the window’s makeKeyAndVisible method.

列表2-3 显示了另一个频繁使用的技术。该例子加载了一个新故事板并实例化它的初始视图控制器。它使用该视图控制器作为一个新窗口的根视图控制器,该窗口将被放置在一个外接屏幕上。要想显示返回的窗口,你的应用程序可以调用窗口的makeKeyAndVisible 方法。

Listing 2-3  Instantiating a view controller from a new storyboard

- (UIWindow*) windowFromStoryboard: (NSString*) storyboardName
                                   onScreen: (UIScreen*) screen
{
    UIWindow *window = [[UIWindow alloc] initWithFrame:[screen bounds]];
    window.screen = screen;
 
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
    MainViewController *mainViewController = [storyboard instantiateInitialViewController];
    window.rootViewController = mainViewController;
 
    // Configure the new view controller here.
 
    return window;
}

Transitioning to a New Storyboard Requires a Programmatic Approach

1)过渡到一个新故事板要求一个程序性方法

Segues connect only scenes that are stored in the same storyboard. To display a view controller from another storyboard, you must explicitly load the storyboard file and instantiate a view controller inside it.

Segues 只连接那些存储在同一个故事板中的场景。 要想显示一个从另一个故事板来的视图控制器,你必须明确地加载故事板文件并在里面实例化一个视图控制器。

There is no requirement that you create multiple storyboards in your app. Here, though, are a few cases where multiple storyboards might be useful to you:

没有要求你在应用程序里必须创建多个故事板。但是,在一些情况下,创建多个故事板可能对你有帮助:

  • You have a large programming team, with different portions of the user interface assigned to different parts of the team. In this case, each sub team owns a storyboard limiting the number of team members working on any specific storyboard’s contents.

    你有一个庞大的编程团队,团队的不同部分被分配有不同用户界面部分。在这种情况下,每个子团队拥有一个故事板限制团队成员中的某人操作任何特定故事板中内容。

  • You purchased or created a library that predefines a collection of view controller types; the contents of those view controllers are defined in a storyboard provided by the library.

    你已经获取或创建了一个库,该库预定义了一个视图控制器类型的集合;那些视图控制器的内容被定义在一个由库提供的一个故事板中。

  • You have content that needs to be displayed on an external screen. In this case, you might keep all of the view controllers associated with the alternate screen inside a separate storyboard. An alternative pattern for the same scenario is to write a custom segue.

    你有需要被显示在一个外接屏幕的内容。在这种情况下,你可能保持所有的与备用屏幕(alternate screen)相关的视图控制器都在一个独立的故事板中。 相同情况下的一种替换方法是编写一个自定义segue。

Containers Automatically Instantiate Their Children

4、容器自动实例化它们的子视图控制器

When a container in a storyboard is instantiated, its children are automatically instantiated at the same time. The children must be instantiated at the same time to give the container controller some content to display.

当一个故事板中的容器被实例化时,它的子视图控制器同时自动被实例化。子视图控制器必须在同时被实例化,以供给容器控制器一些内容显示。

Similarly, if the child that was instantiated is also a container, its children are also instantiated, and so on, until no more containment relationships can be traced to new controllers. If you place a content controller inside a navigation controller inside a tab bar controller, when the tab bar is instantiated, the three controllers are simultaneously instantiated.

类似地,如果被实例化的子视图控制器还是一个容器,其子视图控制器也被同时实例化,一直到没有跟多的包含关系能被追踪到新的视图控制器。如果你把一个内容控制器放置在一个导航控制器,然后把该导航控制器放入一个标签栏控制器,当标签栏被实例化时,3个控制器同时被实例化。

The container and its descendants are instantiated before your view controller is called to configure them. Your source view controller (or app delegate) can rely on all the children being instantiated. This instantiation behavior is important, because your custom configuration code rarely configures the container(s). Instead, it configures the content controllers attached to the container.

容器以及其后裔在你调用视图控制器来配置它们之前被实例化。你的源视图控制器(或应用程序委托)可以依赖所有被实例化的子视图控制器。该实例化行为是重要的,因为你的自定义配置代码很好配置容器。 相反,它配置连接到容器的内容控制器。

Instantiating a Non-Storyboard View Controller

二、实例化一个没有故事板的视图控制器

To create a view controller programmatically without the use of the storyboard, you use Objective-C code to allocate and initialize the view controller. You gain none of the benefits of storyboards, meaning you have to implement additional code to configure and display the new view controller.

要想通过程序创建一个视图控制器,而不使用故事板,你需要使用Objective-C代码类分配和初始化视图控制器。你不能从故事板获取任何好处,意味着你必须实现额外的代码来配置并显示新视图控制器。

Displaying a View Controller’s Contents Programmatically

三、通过程序显示一个视图控制器的内容

For a view controller’s content to be useful, it needs to be displayed on screen. There are several options for displaying a view controller’s contents:

要想让一个视图控制器的内容有用,它需要被显示到屏幕上。这里有一些显示一个视图控制器内容的方法:

  • Make the view controller the root view controller of a window.

    让视图控制器称为窗口的根视图控制器。

  • Make it a child of a visible container view controller.

    让它成为一个可见容器视图控制器的一个子视图控制器。

  • Present it from another visible view controller.

    从另一个可见视图控制器呈现它。

  • Present it using a popover (iPad only).

    使用一个popover(仅限 iPad)来呈现它。

In all cases, you assign the view controller to another object—in this case, a window, a view controller, or a popover controller. This object resizes the view controller’s view and adds it to its own view hierarchy so that it can be displayed.

在所有的情况当中,你把视图控制器分配给另一个对象---在这里是一个窗口,一个视图控制器,或一个弹出菜单控制器。该对象重新调整视图控制器的视图,并把它添加到自己的视图层次结构中然后显示它。

Listing 2-4 shows the most common case, which is to assign the view controller to a window. This code assumes that a storyboard is not being used, so it performs the same steps that are normally done on your behalf by the operating system: It creates a window and sets the new controller as the root view controller. Then it makes the window visible.

列表 2-4 显示了最常用的情况,它把视图控制器分配给一个窗口。该代码假设不使用故事板,因此它跟操作系统替你完成时执行同样的步骤:它创建一个窗口,设置新控制器为根视图控制器。然后让它显示。

Listing 2-4  Installing the view controller as a window’s root view controller

- (void)applicationDidFinishLaunching:(UIApplication *)application {
   UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   levelViewController = [[LevelViewController alloc] init];
   window.rootViewController = levelViewController;
   [window makeKeyAndVisible];
}

Important: Never install the view controller’s view into a view hierarchy directly. To present and manage views properly, the system makes a note of each view (and its associated view controller) that you display. It uses this information later to report view controller–related events to your app. For example, when the device orientation changes, a window uses this information to identify the frontmost view controller and notify it of the change. If you incorporate a view controller’s view into your hierarchy by other means, the system may handle these events incorrectly.

重要提示:绝不能直接把视图控制器的视图装入一个视图层次结构。要想正确呈现并管理视图,系统记录你显示的每个视图(以及它的相关视图控制器)。它在稍后使用该信息来给应用程序报告视图控制器相关的事件。比如,当设备的方向发生改变时,一个窗口使用该信息来认证最前面的视图控制器并通知其该变化。如果你通过其它方式把一个视图控制器的视图并入你的层次结构,系统可能不能正确地处理这些事件。

If you are implementing your own custom container controller, you add another view controller’s view to your own view hierarchy, but you also create a parent-child relationship first. This ensures that events are delivered correctly. See “Creating Custom Container View Controllers.”

如果你正在实现你自己的自定义容器控制器,你把另一个视图控制器的视图添加到你自己的视图层次中,但是你还首先创建了一个父子关系。这样就能确保事件被正确地传递。参见“Creating Custom Container View Controllers.”

Next    Previous