gpt4 book ai didi

objective-c - 如何在 Objective-C 中添加一个拥有自己的 UIViewController 的 subview ?

转载 作者:行者123 更新时间:2023-12-03 05:53:01 25 4
gpt4 key购买 nike

我正在努力处理具有自己的 UIViewControllers 的 subview 。我有一个带有 View (浅粉色)的 UIViewControllertoolbar 上的两个按钮。我希望在按下第一个按钮时显示蓝色 View ,并在按下第二个按钮时显示黄色 View 。如果我只想显示 View 应该很容易。但是蓝色 View 将包含一个表,因此它需要它自己的 Controller 。那是我的第一个类。我从 this SO question 开始,在那里我了解到我需要一个 table 的 Controller 。

所以,我要备份并在这里采取一些婴儿步骤。下面是我的 Utility ViewController(主 View Controller )和其他两个 Controller (蓝色和黄色)的简单起点的图片。想象一下,当 Utility ViewController(主 View )首次显示时,蓝色(默认) View 将显示在粉红色 View 所在的位置。用户将能够单击这两个按钮来回切换,并且永远不会显示粉红色 View 。我只希望蓝色 View 去粉红色 View 所在的地方,黄色 View 去粉红色 View 所在的地方。我希望这是有道理的。

Simple Storyboard image

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

下面是我到目前为止的代码。我想要做的就是在粉红色 View 所在的位置显示蓝色 View 。从我所看到的,我应该只能 addChildViewController 和 addSubView。这段代码不是为我做的。我的困惑越来越好。有人可以帮助我在粉红色 View 所在的位置显示蓝色 View 吗?

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

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

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

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

enter image description here

最佳答案

这篇文章可以追溯到现代 iOS 的早期。它使用当前信息和当前 Swift 语法进行更新。
在今天的 iOS 中 “一切都是容器 View ” 。这是您今天制作应用程序的基本方式。
一个应用程序可能非常简单,以至于它只有一个屏幕。但即使在这种情况下,屏幕上的每个“事物”都是一个容器 View 。
就这么简单...
版本说明
2020 年。如今,您通常只需从单独的 Storyboard加载容器 View ,这非常容易。在这篇文章的底部有解释。如果您不熟悉容器 View ,请先熟悉“经典风格”(“相同 Storyboard”)容器教程。
2021. 更新了语法。使用了 SO 新的“###”漂亮的标题。有关从代码加载的更多详细信息。

(A) 将容器 View 拖到您的场景中...
将容器 View 拖到场景 View 中。 (就像您将拖入任何元素(例如 UIButton)一样。)
容器 View 是这张图片中的棕色东西。它实际上是 里面的 你的 场景 View 。
enter image description here
当您将容器 View 拖到场景 View 中时,Xcode 会自动为您提供 两件事 :

  • 你得到容器 View 里面的 你的场景 View ,并且,
  • 你会得到一个全新的 UIViewController 也就是 就在你的 Storyboard 白色的某个地方。

  • 这两个是 连接 与“共济会符号事物” - 解释如下!

    (B) 单击那个新的 View Controller 。 (所以这是 Xcode 在白色区域的某个地方为你制作的新东西,而不是你场景中的东西。)......而且,改变类!
    真的就是这么简单。
    大功告成。

    这是同样的事情在视觉上解释。
    注意 容器 View (A)
    注意 Controller (B)
    shows a container view and the associated view controller
    单击 B。(那是 B - 不是 A!)
    转到右上角的检查员。注意它说“UIViewController”
    enter image description here
    将其更改为您自己的自定义类,即 UIViewController。
    enter image description here
    所以,我有一个 Swift 类 Snap ,它是一个 UIViewController
    enter image description here
    所以它在检查器中显示“UIViewController”的地方我输入了“Snap”。
    (像往常一样,当您开始输入“Snap...”时,Xcode 会自动完成“Snap”。)
    这就是它的全部 - 你已经完成了。

    如何更改容器 View - 例如,更改为表 View 。
    因此,当您单击以添加容器 View 时,Apple 会自动为您提供一个链接的 View Controller ,位于 Storyboard上。
    目前(2019 年)它碰巧默认为 UIViewController
    这很愚蠢:它应该询问您需要哪种类型。例如,你经常需要一个表格 View 。
    以下是如何将 将其 更改为不同的内容:

    At the time of writing, Xcode gives you a UIViewController by default. Let's say you want a UICollectionViewController instead:

    (i) Drag a container view in to your scene. Look at the UIViewController on the storyboard which Xcode gives you by default.

    (ii) Drag a new UICollectionViewController to anywhere on the main white area of the storyboard.

    (iii) Click the container view inside your scene. Click the connections inspector. Notice there is one "Triggered Segue". Mouse over the "Triggered Segue" and notice that Xcode highlights all of the unwanted UIViewController.

    (iv) Click the "x" to actually delete that Triggered Segue.

    (v) DRAG from that Triggered Segue (viewDidLoad is the only choice). Drag across the storyboard to your new UICollectionViewController. Let go and a pop-up appears. You must select embed.

    (vi) Simply delete all of the unwanted UIViewController. You're done.


    精简版:
  • 删除不需要的 UIViewController。
  • 在 Storyboard的任何位置放置一个新的 UICollectionViewController
  • Control-drag 容器 View 的 0x251819212 连接,将 -DTrigger View 加载到-DTrigger -DTrigger
  • 请务必在弹出窗口中选择“嵌入”。

  • 就这么简单。

    正在输入文本标识符...
    您将拥有以下其中之一 “正方形中的正方形” 共济会符号事物:它位于连接容器 View 与 View Controller 的“弯曲线”上。
    “共济会符号”的东西是 segue
    enter image description here
    通过单击
    上的“共济会符号”事物上的 来选择 segue。
    看看你的右边。
    必须 输入 文本标识符 用于转场。
    您决定名称。 可以是任何文本字符串。 一个不错的选择通常是“segueClassName”。
    如果你遵循这个模式,你所有的 segue 都将被称为 segueClockView、seguePersonSelector、segueSnap、segueCards 等等。
    接下来,您在哪里使用该文本标识符?

    如何连接“到”子 Controller ...
    然后,在代码中,在整个场景的 ViewController 中执行以下操作。
    假设场景中有三个容器 View 。每个容器 View 都有一个不同的 Controller ,比如“Snap”、“Clock”和“Other”。
    最新语法
    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 调用连接一个变量来引用 Controller 。

    如何在“另一个方向”上连接到 parent ...
    假设您“在”放置在容器 View 中的 Controller (示例中的“Snap”)。
    到达您上方的“老板” View Controller (示例中的“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 = parent as? Dash
    }
    严重: 仅适用于 viewDidAppear 或更高版本。在 viewDidLoad 中不起作用。
    你完成了。

    重要提示:这仅适用于容器 View 。
    提示,不要忘记,这仅适用于容器 View 。
    现在有了 Storyboard标识符,在屏幕上弹出新 View 是司空见惯的(而不是在 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)
    使用容器 View 时, 保证 Dash 将成为 Snap 的父 View Controller 。
    然而,当您使用instantiateViewController 时,这是 不一定是 的情况。
    非常令人困惑的是,在 iOS 中,父 View Controller 是 与实例化它的类无关 。 (它 可能 相同,但通常不相同。) self.parent 模式是 仅用于 0.9141 容器 0.9141 View
    (对于instantiateViewController 模式中的类似结果,您必须使用协议(protocol)和委托(delegate),记住委托(delegate)将是一个薄弱环节。)
    请注意,如今从另一个 Storyboard动态加载容器 View 非常容易 - 请参阅下面的最后一节。这往往是最好的方法。

    prepareForSegue 名字不好...
    值得注意的是,“prepareForSegue”是一个 非常糟糕的名字!
    “prepareForSegue”有两个用途:加载容器 View ,以及场景之间的转场。
    但实际上,您很少在场景之间切换!而几乎每个应用程序都有很多很多容器 View ,这是理所当然的。
    如果将“prepareForSegue”称为“loadingContainerView”之类的东西会更有意义。

    超过一个...
    一种常见的情况是:您在屏幕上有一个小区域,您想在其中显示多个不同 View Controller 中的一个。例如,四个小部件之一。
    最简单的方法是:让 四个不同的容器 View 都位于 相同的相同区域 内。在您的代码中,只需隐藏所有四个并打开您想要可见的一个。
    简单。

    容器 View “来自代码”......
    ... 将 Storyboard 动态加载到容器 View 中。
    2019+ 语法
    假设您有一个 Storyboard文件“Map.storyboard”, Storyboard ID 是“MapID”, Storyboard是您的 Map 类的 View Controller 。
    let map = UIStoryboard(name: "Map", bundle: nil)
    .instantiateViewController(withIdentifier: "MapID")
    as! Map
    在你的主场景中有一个普通的 UIView:
    @IBOutlet var dynamicContainerView: UIView!
    Apple 解释 here 添加动态容器 View 必须做的四件事
    addChild(map)
    map.view.frame = dynamicContainerView.bounds
    dynamicContainerView.addSubview(map.view)
    map.didMove(toParent: self)
    (以该顺序。)
    并删除该容器 View :
    map.willMove(toParent: nil)
    map.view.removeFromSuperview()
    map.removeFromParent()
    (也是按这个顺序。)就是这样。
    但是请注意,在该示例中,dynamicContainerView 只是一个固定 View 。它不会改变或调整大小。这只有在您的应用程序从不旋转或其他任何东西时才有效。通常,您必须添加四个常用约束,以便在调整大小时将 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() 以避免出现一行代码!)
    订单必须是提醒
  • 添加 child
  • 添加实际 View
  • 调用didMove

  • 删除其中之一?
    您已将 map 动态添加到持有人,现在您想将其删除。正确且唯一的顺序是:
    map.willMove(toParent: nil)
    map.view.removeFromSuperview()
    map.removeFromParent()
    通常你会有一个持有者 View ,并且你想要交换不同的 Controller 。所以:
    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)
    }

    关于objective-c - 如何在 Objective-C 中添加一个拥有自己的 UIViewController 的 subview ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23399061/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com