关于uitableview:如何在Objective-C中添加一个拥有自己的UIViewController的子视图?

How to add a subview that has its own UIViewController in Objective-C?

我正在与拥有自己的UIViewControllers的颠覆观点作斗争。我有一个带视图(浅粉色)的UIViewController和一个toolbar上的两个按钮。我希望在按下第一个按钮时显示蓝色视图,按下第二个按钮时显示黄色视图。如果我只是想显示一个视图应该很容易。但蓝色视图将包含一个表,因此它需要自己的控制器。那是我的第一堂课。我从这个问题开始,我在哪里学到了我需要一个桌子控制器。

所以,我要退后一步,在这里迈出一些小步。下面是我的工具ViewController(主视图控制器)和其他两个控制器(蓝色和黄色)的简单起点图。设想当实用程序ViewController(主视图)第一次显示时,蓝色(默认)视图将显示在粉色视图所在的位置。用户可以点击这两个按钮来回切换,粉色视图将永远不会显示。我只想让蓝色的视图转到粉色视图所在的位置,而黄色的视图转到粉色视图所在的位置。我希望这是有道理的。

Simple Storyboard image

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

下面是迄今为止我掌握的代码。我所要做的就是显示蓝色视图,粉色视图在其中。从我所看到的情况来看,我应该能够只使用addChildViewController和addsubview。这个代码不是为我做的。我的困惑越来越深了。有人能帮我把蓝色的视图显示在粉红色的地方吗?

除了在VIEWDIDLOAD中显示蓝色视图外,此代码不用于其他任何操作。

idutilityviewcontroller.h(idutilityviewcontroller.h)

1
2
3
4
5
#import <UIKit/UIKit.h>

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

idutilityviewcontroller.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————---

self.aboutvc.aboutview为零。但我把它装进了storyboard里。我还需要实例化它吗?

enter image description here


这篇文章可以追溯到现代iOS的早期,通常有最新的语法,比如目前的swift 4。如果你开始使用iOS、自动布局等,它会让你前进。好的。

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

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

就这么简单…好的。(a)将容器视图拖到场景中…

将容器视图拖到场景视图中。(就像你要拖入一个uibutton一样。)好的。

容器视图是这张图中的褐色部分。它实际上在场景视图中。好的。

enter image description here好的。

当您将容器视图拖动到场景视图中时,Xcode会自动为您提供两种功能:好的。

  • 在场景视图中得到容器视图,好的。

  • 你会得到一个全新的UIViewController,它就在你故事板白色的某个地方。好的。

  • 这两个与"共济会符号"有关-解释如下!好的。(b)点击新的视图控制器(Xcode为你在白色区域的某个地方制作的新东西,而不是你场景中的东西)。还有,换个班!

    这真的很简单。好的。

    你完了。好的。

    这里也有同样的东西可以直观地解释。好的。

    注意(A)上的容器视图。好的。

    注意(B)的控制器。好的。

    shows a container view and the associated view controller好的。

    点击b.(那是b-不是a!)好的。

    去右上角的检查员那里。注意上面写着"uiviewcontroller"好的。

    [enter image description here3]好的。

    将其更改为您自己的自定义类,这是一个uiviewController。好的。

    enter image description here好的。

    所以,我有一个很快的Snap类,它是一个UIViewController。好的。

    enter image description here好的。

    所以当它在inspector中说"uiviewcontroller"时,我输入了"snap"。好的。

    (和往常一样,当您开始键入"snap…"时,xcode将自动完成"snap"。)好的。

    就这些了-你完了。好的。如何将容器视图更改为表视图。

    所以当你点击添加一个容器视图时,苹果会自动给你一个链接视图控制器,位于故事板上。好的。

    实际情况(2017):默认情况下,它将成为UIViewController。好的。

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

    在编写时,Xcode默认提供一个UIViewController。假设你想要一个UICollectionViewController代替:好的。

    (i)将容器视图拖到场景中。看看故事板上的uiviewcontroller,Xcode默认提供给您。好的。

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

    (iii)单击场景内的容器视图。单击连接检查器。请注意,有一个"触发的segue"。将鼠标悬停在"触发的segue"上,注意xcode会突出显示所有不需要的uiviewcontroller。好的。

    (iv)点击"X"实际删除触发的SEgue。好的。

    (v)从触发的SEgue中拖动(只有VIEWDIDLOAD选项)。在情节提要中拖动到新的uiCollectionViewController。放开,弹出一个窗口。必须选择"嵌入"。好的。

    (vi)只需删除所有不需要的uiviewcontroller。你完了。好的。< /块引用>

    短版本:删除不需要的uiviewcontroller。在故事板上放一个新的UICollectionViewController。控件从:容器视图的连接-触发器segue-viewdidLoad拖动到新控制器。确保在弹出窗口中选择"嵌入"。好的。正在输入文本标识符…

    您将拥有其中一个"正方形"共济会符号:它位于连接容器视图和视图控制器的"Bendy线"上。好的。

    "共济会符号"就是世格。好的。

    enter image description here好的。

    点击"共济会符号"选择世格。好的。

    向右看。好的。

    必须为SEgue键入文本标识符。好的。

    你决定名字。它可以是任何文本字符串。明智的选择通常是"segueclassname"。好的。

    如果你遵循这个模式,你的所有段将被称为segueclockview、seguepersonselector、seguesnap、seguecards等等。好的。

    接下来,在哪里使用文本标识符?好的。如何连接到子控制器…

    然后,在代码中,在整个场景的视图控制器中执行以下操作。好的。

    假设场景中有三个容器视图。每个容器视图包含一个不同的控制器,比如"快照"、"时钟"和"其他"。好的。

    最新的swift3语法(2017)好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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调用连接一个变量来引用控制器。好的。如何在"另一个方向"上连接到父级…

    假设您"在"已放置在容器视图中的控制器中(示例中为"捕捉")。好的。

    找到你上面的"老板"视图控制器(示例中的"破折号")可能会让人困惑。幸运的是,这很简单:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 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开发中)是司空见惯的。所以,假设用户想要编辑一些东西…好的。

    1
    2
    3
    4
    5
    6
        // 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的父视图控制器。好的。

    但是,当您使用InstantationViewController时,情况并不一定是这样的。好的。

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

    (对于InstantViewController模式中的类似结果,必须使用协议和委托,记住委托将是弱链接。)好的。PrepareForSegue名字不好…

    值得注意的是,"PrepareForSegue"是一个很坏的名字!好的。

    "PrepareForSegue"用于两个目的:加载容器视图和在场景之间进行分隔。好的。

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

    如果"PrepareForSegue"被称为类似"LoadingContainerView"的东西,那就更有意义了。好的。不止一个…

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

    最简单的方法是:让四个不同的容器视图都位于相同的区域。在您的代码中,只需隐藏所有四个选项,然后打开您希望看到的那个选项。好的。

    在故事板上,有一个空的"holder"uiview,它只保存四个容器视图。然后,通过调整大小或移动"支架",可以同时调整或移动所有四个支架。在您的代码中,只需拥有四个UIView出口,每个出口对应一个容器视图。复制并粘贴上面的代码"如何连接到子控制器",以连接包含的四个视图控制器。好的。注意-故事板参考到达!

    正如Simplgy在下面指出的那样,"IOS9的故事板引用使得容器视图更加令人敬畏。您可以在任何需要的地方定义可重用视图(控制器),并从多个模块化故事板中的任何容器视图引用它。"好的。

    同样要注意的是-相当令人困惑-通常今天您只是不必费心容器视图!好的。

    在许多情况下,您只需使用cx1(2)。好的。

    但请注意上面解释的关于.parent的"哥德基"。容器视图的全部要点是您可以立即简单地确定父链。好的。

    如果您使用故事板引用来使用instantiateViewController#withIdentifier,则必须处理协议和委托(记住委托将是一个弱链接)。但是你可以在任何地方灵活地"随时随地"使用它。好的。

    相比之下,使用"固定"的容器视图是非常简单的,您可以立即连接到父级和子级之间,如上面所述。好的。好啊。


    我看到两个问题。首先,由于你在故事板中制作控制器,你应该用instantiateViewControllerWithIdentifier:来实例化它们,而不是用initWithNibName:bundle:。第二,当您将视图添加为子视图时,应该给它一个框架。所以,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (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];
    }