如何在Objective-C中添加具有自己的UIViewController的子视图?

Pat*_*cia 53 objective-c uitableview uiviewcontroller uicontainerview

我正在为拥有自己的子视图而苦苦挣扎UIViewControllers.我有一个UIViewController视图(浅粉色)和两个按钮toolbar.我希望在按下第一个按钮时显示蓝色视图,按下第二个按钮时显示的黄色视图.如果我只是想显示一个视图,应该很容易.但蓝色视图将包含一个表,因此它需要它自己的控制器.那是我的第一课.我从这个SO问题开始,我知道我需要一个控制器用于表.

所以,我要支持并采取一些婴儿步骤.下面是我的Utility ViewController(主视图控制器)和另外两个控制器(蓝色和黄色)的简单起点图片.想象一下,当ViewController首次显示Utility (主视图)时,将显示粉红色视图所在的蓝色(默认)视图.用户可以单击两个按钮来回移动,粉红色视图将永远不会显示.我只想将蓝色视图移到粉红色视图的位置,将黄色视图移到粉红色视图所在的位置.我希望这是有道理的.

简单的故事板图像

我正在尝试使用addChildViewController.从我所看到的,有两种方法可以做到这一点:容器视图storyboardaddChildViewController编程.我想以编程方式进行.我不想使用NavigationController或Tab栏.我只想添加控制器,并在按下相关按钮时将正确的视图推入粉红色视图.

下面是我到目前为止的代码.我想要做的就是显示粉红色视图所在的蓝色视图.从我所看到的,我应该能够addChildViewController和addSubView.这段代码不适合我.我的困惑是让我变得更好.有人可以帮我看到粉红色视图所在的蓝色视图吗?

除了在viewDidLoad中显示蓝色视图外,此代码不用于执行任何操作.

IDUtilityViewController.h

#import <UIKit/UIKit.h>

@interface IDUtilityViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *utilityView;
@end
Run Code Online (Sandbox Code Playgroud)

IDUtilityViewController.m

#import "IDUtilityViewController.h"
#import "IDAboutViewController.h"

@interface IDUtilityViewController ()
@property (nonatomic, strong) IDAboutViewController *aboutVC;
@end

@implementation IDUtilityViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.aboutVC = [[IDAboutViewController alloc]initWithNibName:@"AboutVC" bundle:nil];
    [self addChildViewController:self.aboutVC];
    [self.aboutVC didMoveToParentViewController:self];
    [self.utilityView addSubview:self.aboutVC.aboutView];
}

@end
Run Code Online (Sandbox Code Playgroud)

- - - - - - - - - - - - - 编辑 - - - - - - - - - - - - -------

self.aboutVC.aboutView是零.但我把它连接起来了storyboard.我还需要实例化吗?

在此输入图像描述

Fat*_*tie 132

这篇帖子可以追溯到现代iOS的早期阶段,通常具有最新的语法,例如目前的Swift 4.如果您从iOS,自动布局等开始,它将帮助您.

在今天的iOS中,"一切都是容器视图".这是您今天制作应用程序的基本方式.

应用程序可能非常简单,只有一个视图.但即使在这种情况下,屏幕上的每个"东西"都是一个容器视图.

就这么简单......


(A)将容器视图拖到场景中......

将容器视图拖到场景视图中.(就像你会拖入UIButton一样.)

容器视图是此图像中的棕色内容.它实际上你的场景视图中.

在此输入图像描述

将容器视图拖到场景视图中时,Xcode会自动为您提供两件事:

  1. 在场景视图中获得容器视图,并且,

  2. 你会得到一个全新的UIViewController,只是坐在故事板白色的某个地方.

这两者是相连的"共济会的符号"的事情-下面解释!


(B)点击那个新的视图控制器(Xcode为你在白色区域的某个地方制作的新东西,而不是场景中的东西)......然后,改变课程!

这真的很简单.

你完成了.


这是视觉上解释的相同的事情.

注意容器视图(A).

注意控制器(B).

显示容器视图和关联的视图控制器

点击B.(那是B - 不是A!)

转到右上角的检查员.注意它说"UIViewController"

[在此输入图像描述] [3]

将其更改为您自己的自定义类,这是一个UIViewController.

在此输入图像描述

所以,我有一个雨燕类Snap这是一个UIViewController.

在此输入图像描述

所以在Inspector中我说"UIViewController"的地方我输入了"Snap".

(像往常一样,当你开始输入"Snap ..."时,Xcode会自动完成"Snap".)

这就是它的全部 - 你已经完成了.


如何更改容器视图 - 例如,更改为表视图.

因此,当您单击以添加容器视图时,Apple会自动为您提供位于故事板上的链接视图控制器.

碰巧(2017年):UIViewController默认情况下它是一个.

这很愚蠢:它应该问你需要哪种类型.例如,通常需要表视图.以下是如何将其更改为不同的内容:

在撰写本文时,Xcode UIViewController默认为您提供.让我们说你想要一个UICollectionViewController代替:

(i)将容器视图拖到场景中.查看Xcode默认为您提供的故事板上的UIViewController.

(ii)将新的拖动UICollectionViewController到故事板的主要白色区域的任何位置.

(iii)单击场景中的容器视图.单击连接检查器.请注意,有一个"触发的塞格". 将鼠标悬停在"触发的Segue"上,注意Xcode 突出显示所有不需要的UIViewController.

(iv)单击"x"以实际删除触发的Segue.

(v)触发的Segue中拖出(viewDidLoad是唯一的选择).将故事板拖到新的UICollectionViewController中.放手,弹出窗口出现.您必须选择嵌入.

(vi)只需删除所有不需要的UIViewController.你完成了.

简短版本:删除不需要的UIViewController.UICollectionViewController在故事板上添加一个新内容.控制 - 拖动:容器视图的Connections - Trigger Segue - viewDidLoad,to,您的新控制器.一定要在弹出窗口中选择"嵌入".


输入文本标识符......

您将拥有其中一个"方形正方形" Masonic符号的东西:它位于连接容器视图和视图控制器的"弯曲线"上.

"共济会符号"的东西就是塞格.

在此输入图像描述

通过点击选择SEGUE 的"共济会标志"的事情.

向右看.

必须输入segue 的文本标识符.

你决定这个名字.它可以是任何文本字符串.明智的选择通常是"segueClassName".

如果您遵循该模式,您的所有segue将被称为segueClockView,seguePersonSelector,segueSnap,segueCards等.

接下来,您在哪里使用该文本标识符?


如何连接'到'子控制器...

然后,在代码中,在整个场景的ViewController中执行以下操作.

假设您在场景中有三个容器视图.每个容器视图都包含一个不同的控制器,比如"Snap","Clock"和"Other".

最新的Swift3语法(2017)

var snap:Snap?
var clock:Clock?
var other:Other?

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.identifier == "segueSnap")
            { snap = (segue.destination as! Snap) }
    if (segue.identifier == "segueClock")
            { clock = (segue.destination as! Clock) }
    if (segue.identifier == "segueOther")
            { other = (segue.destination as! Other) }
}
Run Code Online (Sandbox Code Playgroud)

就这么简单.您使用该prepareForSegue调用连接变量以引用控制器.


如何在"其他方向"连接,直到父母......

假设您已经放入容器视图中的控制器(示例中为"Snap").

到达您上方的"boss"视图控制器(示例中为"Dash")可能会令人困惑.幸运的是,这很简单:

// Dash is the overall scene.
// Here we are in Snap. Snap is one of the container views inside Dash.

class Snap {

var myBoss:Dash?    
override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear
    super.viewDidAppear(animated)
    myBoss = self.parent as? Dash
}
Run Code Online (Sandbox Code Playgroud)

关键:仅适用于viewDidAppear或稍后.不会工作viewDidLoad.

你完成了.


重要说明:适用于容器视图.

重要的高级提示:不要忘记,这仅适用于容器视图.

现在,通过故事板标识符,只需在屏幕上弹出新视图(而不是在Android开发中).所以,让我们说用户想要编辑一些东西......

    // let's just pop a view on the screen.
    // this has nothing to do with container views
    //
    let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit
    e.modalPresentationStyle = .overCurrentContext
    self.present(e, animated: false, completion: nil)
Run Code Online (Sandbox Code Playgroud)

使用容器视图时,我们保证 Dash将成为Snap的父视图控制器.

但是,当您使用instantiateViewController时,这不是必须的.

非常容易混淆,在iOS中,父视图控制器实例化它的类无关.(它可能是相同的,但通常不一样.)该self.parent模式适用于容器视图.

(对于instantiateViewController模式中的类似结果,您必须使用协议和委托,记住委托将是一个弱链接.)


prepareForSegue名字不好......

值得注意的是"prepareForSegue"是一个非常糟糕的名字!

"prepareForSegue"用于两个目的:加载容器视图,以及场景之间的segue.

但在实践中,你很少在场景之间徘徊!几乎每个应用程序都有很多很多容器视图.

如果"prepareForSegue"被称为"loadingContainerView",那将更有意义.


超过一个...

常见的情况是:屏幕上有一个小区域,您希望在其中显示多个不同的视图控制器之一.例如,四个小部件之一.

最简单的方法:只有四个不同的容器视图都位于相同的相同区域.在您的代码中,只需隐藏所有四个并打开您想要的那个.

在故事板上,有一个空的"持有者"UIView,它只保存四个容器视图.然后,您可以通过调整或移动"支架"来一次调整或移动所有四个.在您的代码中,只需要四个Map插座,每个插座用于每个容器视图.复制并粘贴上面的代码"如何连接到子控制器",以连接四个包含的视图控制器.


注 - 故事板参考文献到达!

正如SimplGy在下面指出的那样"iOS 9的故事板引用使容器视图更加精彩.您可以在任何地方定义可重用的视图(控制器),并从多个模块化故事板中的任何容器视图中引用它."

还要注意 - 相当容易混淆 - 今天通常你只是不打扰容器视图!

你只是dynamicContainerView在很多情况下.

但请注意UIViewController上面解释的"gotchya" .容器视图的重点在于您可以立即确保父链.

如果你(A)使用故事板参考,你必须搞乱协议和委托(记住委托将是一个薄弱环节).但随后你可以在任何地方灵活地"动态"使用它.

相比之下,使用"固定",容器视图非常简单,您可以立即在父母和孩子之间进行连接,如上所述.

  • iOS 9的[故事板参考](https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/Chapters/AddSBReference.html)使容器视图更加精彩.您可以在任何位置定义可重用的视图(控制器),并从多个模块化故事板中的任何容器视图中引用它. (2认同)

rde*_*mar 5

我看到两个问题。首先,由于要在情节提要中创建控制器,因此应使用instantiateViewControllerWithIdentifier:而不是实例化它们initWithNibName:bundle:。其次,将视图添加为子视图时,应为其提供一个框架。所以,

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:@"aboutVC"]; // make sure you give the controller this same identifier in the storyboard
    [self addChildViewController:self.aboutVC];
    [self.aboutVC didMoveToParentViewController:self];
    self.aboutVC.view.frame = self.utilityView.bounds;
    [self.utilityView addSubview:self.aboutVC.aboutView];
}
Run Code Online (Sandbox Code Playgroud)

  • 是否需要设置框架?还是会根据情节提要为视图提供默认框架? (2认同)
  • @Brian,如果要添加的视图是全屏视图,则可能没有必要,但是我通常会以任何方式添加它以确保。另外,在这种情况下,我不确定self.utilityView是工具栏下方的完整视图还是视图的一部分,在这种情况下,您可能确实需要设置框架。 (2认同)
  • @ JoeBlow,OP提到在情节提要中使用容器视图,但表示他们想用代码来实现。 (2认同)