当前位置: 首页 > 知识库问答 >
问题:

如何添加一个子视图,它有自己的UIViewController在Objecte-C?

吴同
2023-03-14

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

所以,我要后退一步。下面是一个简单起点的图片,我的实用程序ViewController(主视图控制器)和另外两个控制器(蓝色和黄色)。想象一下,当第一次显示实用程序ViewController(主视图)时,蓝色(默认)视图将显示在粉色视图所在的位置。用户将能够点击这两个按钮来回移动,粉色视图将永远不会显示。我只想要蓝色的风景去粉色的风景,黄色的风景去粉色的风景。我希望这是有道理的。

我正在尝试使用addChildViewController。据我所见,有两种方法可以做到这一点:以编程方式在情节提要addChildViewController中创建容器视图。我想用编程的方式来做。我不想使用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

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

自我。关于TVC。aboutView为零。但是我把它连接到了故事板上。我还需要实例化它吗?


共有2个答案

糜单弓
2023-03-14

我看到两个问题。首先,由于您要在情节提要中创建控制器,因此应该使用instantialeviewcontrollerwhiteIdentifier:,而不是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];
}
朱硕
2023-03-14

这篇文章可以追溯到现代iOS的早期。它使用当前信息和当前Swift语法进行更新。

iOS今天“一切都是容器观”。这是你今天制作应用程序的基本方式。

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

就这么简单。。。

版本说明

2020年。如今,您通常只需从单独的故事板加载容器视图,这非常容易。在这篇文章的底部有解释。如果您是容器视图的新手,也许可以先熟悉“经典风格”(“相同的故事板”)容器教程。

2021、更新语法。用了这么多新的####漂亮的标题。有关从代码加载的更多详细信息。

将容器视图拖到场景视图中。(就像您将拖动任何元素,如UIButton一样。)

容器视图是此图中的棕色对象。它实际上在场景视图中。

将容器视图拖动到场景视图中时,Xcode会自动提供两种功能:

>

  • 在场景视图中获得容器视图,

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

    这两者都与“共济会符号”有关——解释如下!

    就这么简单。

    你完蛋了。

    这是视觉上解释的同样的事情。

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

    请注意位于(B)的控制器。

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

    去右上方的检查员那里。注意上面写着"UIViewController"

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

    因此,我有一个Swift类Snap,它是一个UIViewController

    在我输入“Snap”的检查器中显示“UIViewController”。

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

    这就是全部-你完蛋了。

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

    目前(2019年),默认情况下,它恰好成为一个UIViewController

    这很愚蠢:它应该问你需要哪种类型。例如,通常需要一个表视图。

    下面是如何将其更改为不同的内容:

    在编写本文时,Xcode默认为您提供了一个UIViewController。假设您想要一个UICollectionViewController

    (i) 将容器视图拖动到场景中。查看序列图像板上的UIViewController,默认情况下Xcode为您提供该控制器。

    (ii)将新的UICollectionViewController拖动到情节提要主白色区域的任何位置。

    (iii)单击场景中的容器视图。单击连接检查器。请注意,有一个“触发序列”。将鼠标移到“触发序列”上,注意Xcode高亮显示了所有不需要的UIViewController。

    (iv)单击“x”以实际删除触发的序列。

    (v) 从触发的序列中拖动(viewDidLoad是唯一的选择)。在情节提要上拖动到新的UICollectionViewController。放开,一个弹出窗口出现。您必须选择“嵌入”。

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

    简短版本:

    >

  • 删除不需要的UIViewController。

    将新的UICollectionViewController放在情节提要上的任意位置。

    控件从容器视图的Connections-Trigger Segue-viewDidLoad拖动到新控制器。

    确保在弹出窗口中选择“嵌入”。

    就这么简单。

    你会有一个“正方形中的正方形”共济会符号:它在连接容器视图和视图控制器的“弯曲线”上。

    “共济会符号”就是赛格。

    点击“共济会符号”来选择乐段。

    看你的右边。

    您必须为segue键入文本标识符。

    你决定名字。它可以是任何文本字符串。一个好的选择通常是"segueClassName"。

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

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

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

    假设场景中有三个容器视图。每个容器视图持有不同的控制器,如“快照”、“时钟”和“其他”。

    最新语法

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

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

    假设您“在”已放置在容器视图中的控制器中(示例中为“快照”)。

    到达您上方的“boss”视图控制器(示例中为“破折号”)可能会让人困惑。幸运的是,这很简单:

    // 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 = parent as? Dash
    }
    

    关键:仅适用于视图显示或更高版本。在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的父视图控制器。

    但是,当您使用InstanceDeviceController时,情况不一定如此。

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

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

    请注意,现在从另一个故事板动态加载容器视图非常容易——参见下面的最后一节。这通常是最好的方法。

    值得注意的是,“准备就绪”是一个非常糟糕的名字!

    “prepareForSegue”用于两个目的:加载容器视图,以及在场景之间进行分段。

    但是在实践中,你很少在场景之间切换!然而,几乎每个应用程序都有很多很多容器视图,这是理所当然的。

    如果“prepareForSegue”被称为“loadingContainerView”之类的话,那就更有意义了。

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

    最简单的方法是:在同一区域内放置四个不同的容器视图。在您的代码中,只需隐藏所有四个,然后打开希望可见的一个。

    容易的

    假设您有一个情节提要文件“Map.storyboard”,情节提要ID为“MapID”,并且情节提要是Map类的视图控制器。

    let map = UIStoryboard(name: "Map", bundle: nil)
               .instantiateViewController(withIdentifier: "MapID")
               as! Map
    

    在你的主场景中有一个普通的UIView:

    @IBOutlet var dynamicContainerView: UIView!
    

    Apple在此解释添加动态容器视图需要做的四件事

    addChild(map)
    map.view.frame = dynamicContainerView.bounds
    dynamicContainerView.addSubview(map.view)
    map.didMove(toParent: self)
    

    (按此顺序。)

    要删除该容器视图,请执行以下操作:

    map.willMove(toParent: nil)
    map.view.removeFromSuperview()
    map.removeFromParent()
    

    (也按顺序排列。)就这样。

    但是请注意,在该示例中,DynamicContainerView只是一个固定视图。它不会改变或调整大小。只有当你的应用从不旋转或其他任何事情时,这才有效。通常,您必须添加四个常见的约束,以便在它调整大小时简单地将map.view保留在DynamicContainerView中。事实上,这是任何iOS应用程序都需要的“世界上最方便的扩展”,

    extension UIView {
        
        // it's basically impossible to make an iOS app without this!
        
        func bindEdgesToSuperview() {
            
            guard let s = superview else {
                preconditionFailure("`superview` nil in bindEdgesToSuperview")
            }
            
            translatesAutoresizingMaskIntoConstraints = false
            leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
            trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
            topAnchor.constraint(equalTo: s.topAnchor).isActive = true
            bottomAnchor.constraint(equalTo: s.bottomAnchor).isActive = true
        }
    }
    

    因此,在任何实际应用程序中,上述代码都是:

    addChild(map)
    dynamicContainerView.addSubview(map.view)
    map.view.bindEdgesToSuperview()
    map.didMove(toParent: self)
    

    (有些人甚至做了一个扩展名.addSubviewAndBindEdgesToSuperview(),以避免那里出现一行代码!)

    提醒必须删除订单

    • 添加子项
    • 添加实际视图
    • 打电话给我

    您已将map动态添加到保持架中,现在要将其删除。正确且唯一的顺序是:

    map.willMove(toParent: nil)
    map.view.removeFromSuperview()
    map.removeFromParent()
    

    通常情况下,您会有一个holder视图,并且您希望交换不同的控制器。因此:

    var current: UIViewController? = nil
    private func _install(_ newOne: UIViewController) {
        if let c = current {
            c.willMove(toParent: nil)
            c.view.removeFromSuperview()
            c.removeFromParent()
        }
        current = newOne
        addChild(current!)
        holder.addSubview(current!.view)
        current!.view.bindEdgesToSuperview()
        current!.didMove(toParent: self)
    }
    

  •  类似资料:
    • 我不知道这是否是搜索“在子视图中添加UIViewController”的正确键。正如您在我的图像中看到的,有两个ViewController,主控制器和第二个控制器。主控制器内部有一个UIView(蓝色背景色)。在UIView中,我想在UIView中添加第二个ViewController。我有这个代码,但不起作用。 这是我的密码 我想知道这是否可行?我知道在xib文件中工作,我不知道在google

    • 如何添加mathwidget作为一个子视图内另一个UIViewController目前,mathwidget工作正常时,加载UIViewController. let subViewEE=MathWidgetClassName()self.present(subViewEE,动画:真,完成:零) 但是,当我试图将其添加为当前视图控制器中的子视图时,什么都没有显示,下面是代码: 有人能帮助在当前UI

    • JFinalConfig 是 JFinal 的核心配置,详情: https://www.jfinal.com/doc/2-1 ,其内容如下: public class DemoConfig extends JFinalConfig { public void configConstant(Constants me) {} public void configRoute(Routes

    • 我创建了一个几乎为空的UIViewController类,名为。在视图中,我设置标题并添加一个关闭按钮到导航我tem.leftBarButtonItem。 我的如下所示: 显示viewController时,其视图的背景为黑色。如何设置它的视图以用空视图填充屏幕——就像在故事板中设置UIViewController一样? 我已经尝试将以下内容添加到视图,但是视图仍然是黑色的:

    • 我尝试创建自己的taglib,它扩展了现有的taglib:liferay-UI中的input-asset-links。因此,我在my-ext-web中的web-inf/tld中创建了文件liferay-ui-ext.tld,其中xml如下: 我还在web.xml中添加了以下代码: 13:48:19,661错误[http-bio-8080-exec-22][includetaG:129]当前URL/

    • 问题内容: 我没有找到有关此问题的文章,但没有一个解决我的问题。 就像我说的那样。 ViewControllerA ViewControllerB 我试图将添加为的子视图,但是它 抛出类似“ ” 的错误。 下面是代码… ViewControllerA ViewControllerB只是一个带有标签的简单屏幕。 ViewControllerB EDIT 根据用户答案的​​建议解决方案,ViewCon