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

时间:2014-04-30 22:21:57

标签: objective-c uitableview uiviewcontroller uicontainerview

我正在为拥有自己的UIViewControllers的子视图而苦苦挣扎。我有UIViewController的视图(浅粉色)和toolbar上的两个按钮。我希望在按下第一个按钮时显示蓝色视图,按下第二个按钮时显示的黄色视图。如果我只想显示一个视图,应该很容易。但蓝色视图将包含一个表,因此它需要它自己的控制器。那是我的第一课。我从this SO question开始,在那里我学会了我需要一个桌面控制器。

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

Simple Storyboard image

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

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

此代码除了在viewDidLoad中显示蓝色视图外,不打算执行任何操作。

IDUtilityViewController.h

#import <UIKit/UIKit.h>

@interface IDUtilityViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *utilityView;
@end

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

-------------------------- EDIT ----------------- -------------

self.aboutVC.aboutView为零。但是我在storyboard中连接了它。我还需要实例化吗?

enter image description here

2 个答案:

答案 0 :(得分:128)

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

今天的iOS &#34;一切都是容器视图&#34; 。这是您今天制作应用程序的基本方式。

应用程序可能非常简单,只有一个视图。但即使在这种情况下,每个&#34;&#34;在屏幕上是一个容器视图。

这很容易......


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

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

容器视图是此图像中的棕色内容。它实际上是内部您的场景视图。

enter image description here

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

  1. 您在场景视图中获得容器视图

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

  3. 这两个 已连接 与&#34;共济会符号&#34;事情 - 在下面解释!


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

    真的那么简单。

    您已完成。


    以下是视觉上解释的相同内容。

    请注意(A)处的 容器视图

    请注意(B)处的 控制器

    shows a container view and the associated view controller

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

    转到右上方的检查员。注意它说&#34; UIViewController&#34;

    [enter image description here] [3]

    将其更改为您自己的自定义类,即UIViewController。

    enter image description here

    所以,我有一个Swift类Snap,它是UIViewController

    enter image description here

    所以说它&#34; UIViewController&#34;在检查员中,我输入了&#34; Snap&#34;。

    (像往常一样,Xcode将自动完成&#34; Snap&#34;当您开始输入&#34; Snap ...&#34;。)

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


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

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

    碰巧(2017年):它默认为UIViewController

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

      

    在撰写本文时,Xcode默认为您提供UIViewController。我们假设你想要一个UICollectionViewController

         

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

         

    (ii)将新的UICollectionViewController拖到故事板主白区的任意位置。

         

    (iii)单击场景中的容器视图。单击连接检查器。请注意,有一个&#34; Triggered Segue&#34;。 鼠标悬停&#34;触发的Segue&#34;并注意到Xcode 突出显示所有不需要的UIViewController。

         

    (iv)点击&#34; x&#34;实际上删除那个被触发的Segue。

         

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

         

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

    简短版本:删除不需要的UIViewController。在故事板上放置一个新的UICollectionViewController。从以下位置进行控制 - 拖动:容器视图的连接 - 触发器调用 - viewDidLoad,到您的新控制器。一定要选择&#34;嵌入&#34;在弹出窗口上。


    输入文本标识符...

    你将在正方形&#34; 共济会符号中拥有其中一个&#34; square:它位于&#34;弯曲线&#34;将容器视图与视图控制器连接。

    &#34;共济会的符号&#34;事情就是塞古。

    enter image description here

    点击 &#34; masonic symbol&#34;选择segue。的事情。

    向右看。

    必须键入segue的文本标识符

    您决定姓名。 它可以是任何文本字符串。明智的选择通常是&#34; segueClassName&#34;。

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

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


    如何连接&#39;儿童控制器......

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

    假设您在场景中有三个容器视图。每个容器视图都包含一个不同的控制器,例如&#34; Snap&#34;,&#34; Clock&#34;和&#34;其他&#34;。

    最新的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) }
    }
    

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


    如何在&#39;其他方向连接,直到父母......

    说你&#34;在#34;放在容器视图中的控制器(&#34; Snap&#34;在示例中)。

    去老板&#34;这可能会令人困惑。查看您上方的控制器(&#34; Dash&#34;在示例中)。幸运的是,这很简单:

    // 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
    }
    

    严重:仅适用于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)
    

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

    但是,当您使用instantiateViewController时,非必然情况

    非常令人困惑的是,在iOS中,父视图控制器与实例化它的类不相关。 (可能相同,但通常不一样。)容器视图的self.parent模式

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


    prepareForSegue名字不好......

    值得注意的是&#34; prepareForSegue&#34;是一个非常糟糕的名字!

    &#34; prepareForSegue&#34;用于两个目的:加载容器视图,以及场景之间的隔离。

    但实际上,你很少在场景之间徘徊!几乎每个应用都有很多很多容器视图。

    如果&#34; prepareForSegue&#34;那将更有意义。被称为&#34; loadingContainerView&#34;。


    不止一个......

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

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

    在故事板上,有一个空的&#34;持有者&#34; UIView,它只保存四个容器视图。然后,您可以通过调整或移动&#34;持有者&#34;来一次调整或移动所有四个。在您的代码中,只需要有四个UIView个出口,每个出口对应一个容器视图。复制并粘贴上面的代码,&#34;如何连接到子控制器&#34;,以连接四个包含的视图控制器。


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

    正如SimplGy在下面指出&#34; iOS 9&#39; Storyboard References使容器视图更加精彩。您可以在任何位置定义可重用的视图(控制器),并从多个模块化故事板中的任何容器视图中引用它。&#34;

    请注意 - 相当令人困惑 - 通常今天你只是不打扰容器视图!

    在许多情况下,你只是instantiateViewController#withIdentifier

    但请注意&#34; gotchya&#34;关于上面解释的.parent。容器视图的重点在于您可以立即确定父链。

    如果你使用故事板引用与instantiateViewController#withIdentifier一起使用,你必须搞乱一个协议和一个委托(记住委托将是一个弱链接)。但是你可以随时使用它#34;灵活地在任何地方。

    相比之下,使用&#34; fixed&#34;可以说容器视图非常简单,如上所述,您可以立即连接父母和孩子。

答案 1 :(得分: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];
}