如何在Objective-C中添加具有自己的UIViewController的子视图? [英] How to add a subview that has its own UIViewController in Objective-C?

查看:79
本文介绍了如何在Objective-C中添加具有自己的UIViewController的子视图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am struggling with subviews that have their own UIViewControllers. I have a UIViewController with a view (light pink) and two buttons on a toolbar. I want blue view to display when the first button is pressed and the yellow view to display with the second button is pressed. Should be easy if I just wanted to display a view. But the blue view will contain a table, so it needs it's own controller. That was my first lesson. I started off with this SO question where I learned I needed a controller for the table.

因此,我将在这里备份并采取一些步骤.下面是使用Utility ViewController(主视图控制器)和其他两个控制器(蓝色和黄色)的简单起点的图片.想象一下,当第一次显示Utility ViewController(主视图)时,蓝色(默认)视图将显示在粉红色视图所在的位置.用户将能够单击两个按钮来回移动,并且永远不会显示粉红色视图.我只希望蓝色视图移到粉红色视图所在的位置,而黄色视图移到粉红色视图所在的位置.我希望这是有道理的.

So, I am going to back up and take some baby steps here. Below is a picture of a simple starting point with my Utility ViewController (the main view controller) and the other two controllers (blue and yellow). Imagine that when the Utility ViewController (the main view) is first displayed the blue (default) view will be displayed where the pink view is located. Users will be able to click the two buttons to go back and forth and the pink view will NEVER be displayed. I just want the blue view to go where the pink view is and the yellow view to go where the pink view is. I hope this makes sense.

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

I'm trying to use addChildViewController. From what I have seen, there are two ways to do this: The Container View in the storyboard or addChildViewController programmatically. I want to do it programmatically. I don't want to use a NavigationController or a Tab bar. I just want to add the controllers and shove the correct view into the pink view when the associated button is pressed.

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

Below is the code I have so far. All I want to do is display the blue view where the pink view is. From what I have seen I should be able to just addChildViewController and addSubView. This code is not doing that for me. My confusion is getting the better of me. Can somebody help me get the blue view displayed where the pink view is?

除了在viewDidLoad中显示蓝色视图之外,此代码无其他用途.

This code is not intended to do anything other than display the blue view in 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中将其连接起来.我还需要实例化它吗?

The self.aboutVC.aboutView is nil. But I wired it up in the storyboard. Do I still need to instantiate it?

推荐答案

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

This post dates from the early days of modern iOS. It is updated with current information and the current Swift syntax.

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

In iOS today "everything is a container view". It is the basic way you make apps today.

一个应用可能很简单,只有一个屏幕.但是即使在这种情况下,屏幕上的每个事物"都是容器视图.

An app may be so simple that it has just the one screen. But even in that case, each "thing" on the screen is a container view.

就这么简单...

将容器视图拖动到场景视图中. (就像您拖入UIButton之类的任何元素一样.)

Drag a container view into your scene view. (Just as you would drag in any element such as a UIButton.)

该视图中的容器视图是棕色的东西.它实际上是在场景视图内内部.

The container view is the brown thing in this image. It is actually inside your scene view.

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

When you drag a container view into your scene view, Xcode automatically gives you two things:

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

  1. You get the container view inside your scene view, and,

您会得到一个全新的UIViewController,它是 正好位于故事板白色的某个地方 .

you get a brand-new UIViewController which is just sitting around somewhere on the white of your storyboard.

二者通过 连接 ,在下面进行解释!

The two are connected with the "Masonic Symbol Thing" - explained below!

就这么简单.

您已完成.

这是视觉上解释的同一件事.

Here's the same thing explained visually.

(A)处注意 容器视图 .

Notice the container view at (A).

(B)处通知 控制器 .

Notice the controller at (B).

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

Click on B. (That's B - not A!)

转到右上方的检查器.注意它显示"UIViewController"

Go to the inspector at the top right. Notice it says "UIViewController"

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

Change it to your own custom class, which is a UIViewController.

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

So, I have a Swift class Snap which is a UIViewController.

所以我在快照"中键入的检查器中显示"UIViewController"的地方.

So where it says "UIViewController" in the Inspector I typed in "Snap".

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

(As usual, Xcode will auto-complete "Snap" as you start typing "Snap...".)

这就是全部-您已完成.

That's all there is to it - you're done.

因此,当您单击以添加容器视图时,Apple会自动为您提供一个位于情节提要上的链接视图控制器.

So when you click to add a container view, Apple automatically gives you a linked view controller, sitting on the storyboard.

目前(2019年),默认情况下将其设置为UIViewController.

Currently (2019) it happens to make it a UIViewController by default.

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

That's silly: it should ask which type you need. For example, often you need a table view.

以下是将其更改为其他内容的方法:

Here's how to change it to something different:

在撰写本文时,Xcode默认为您提供UIViewController.假设您要使用UICollectionViewController代替:

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

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

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

(ii)将新的UICollectionViewController拖到情节提要的主要白色区域上的任意位置.

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

(iii)单击场景内的容器视图.单击连接检查器.注意,有一个"Triggered Segue". 将鼠标悬停在"Triggered Segue"上,并注意到Xcode突出显示了所有不需要的UIViewController.

(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)单击"x"以实际删除触发了Segue的事件.

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

(v) DRAG (来自触发的Segue)(viewDidLoad是唯一选择).在情节提要中拖动到新的UICollectionViewController.松手,将弹出一个窗口.您必须选择嵌入.

(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)只需删除所有不需要的UIViewController.完成了.

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

简短版本:

  • 删除不需要的UIViewController.

  • delete the unwanted UIViewController.

在情节提要板上的任何地方放置一个新的UICollectionViewController.

Put a new UICollectionViewController anywhere on the storyboard.

按住Control键并拖动,到您的新控制器.

请确保在弹出窗口上选择嵌入".

Be sure to select "embed" on the popup.

就这么简单.

您将拥有以下正方形中的正方形" 中的一种:共济会符号:它位于将容器视图与视图控制器连接起来的折线"上.

You will have one of these "square in a square" Masonic symbol things: it is on the "bendy line" connecting your container view with the view controller.

共济会符号"是 segue .

通过点击共济会符号上的东西来选择segue.

Select the segue by clicking on the "masonic symbol" thing.

向右看.

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

您确定名称. 它可以是任何文本字符串.一个不错的选择通常是"segueClassName".

You decide on the name. It can be any text string. A good choice is often "segueClassName".

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

If you follow that pattern, all your segues will be called segueClockView, seguePersonSelector, segueSnap, segueCards and so on.

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

Next, where do you use that text identifier?

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

Then, do the following, in code, in the ViewController of the whole scene.

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

Let's say you have three container views in the scene. Each container view holds a different controller, say "Snap", "Clock" and "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调用连接变量以引用控制器.

It's that simple. You connect a variable to refer to the controllers, using the prepareForSegue call.

假设您处于"放置在容器视图中的控制器中(示例中为"Snap").

Say you're "in" the controller which you have put in a container view ("Snap" in the example).

转到您上方的老板"视图控制器(示例中为破折号")可能会造成混淆.幸运的是,它很简单:

It can be a confusing to get to the "boss" view controller above you ("Dash" in the example). Fortunately, it is this simple:

// 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中将不起作用.

Critical: Only works from viewDidAppear or later. Will not work in viewDidLoad.

您完成了.

提示,请记住,这仅适用于容器视图.

Tip, don't forget, that only works for container views.

这几天有了情节提要标识符,在屏幕上弹出新视图是很平常的事(而不是在Android开发中).因此,假设用户要编辑某些内容...

These days with storyboard identifiers, it's commonplace to just pop new views on the screen (rather as in Android development). So, let's say the user wants to edit something...

    // 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的父视图控制器.

When using a container view, IT IS GUARANTEED that Dash will be the parent view controller of Snap.

但是,当您使用InstantiateViewController时,不需要这种情况.

However that is NOT NECESSARILY THE CASE when you use instantiateViewController.

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

Very confusingly, in iOS the parent view controller is not related to the class which instantiated it. (It might be the same, but usually it is not the same.) The self.parent pattern is only for container views.

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

(For a similar result in the instantiateViewController pattern, you have to use a protocol and a delegate, remembering that the delegate will be a weak link.)

请注意,尽管最近这些天很容易从另一个情节提要中动态加载容器视图-请参阅下面的最后一节.这通常是最好的方法.

Note though that these days it's pretty easy to dynamically load a container view from another storyboard - see last section below. It's often the best way.

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

It's worth noting that "prepareForSegue" is a really bad name!

"prepareForSegue"用于两个目的:加载容器视图,以及在场景之间进行筛选.

"prepareForSegue" is used for two purposes: loading container views, and, segueing between scenes.

但是在实践中,您很少在场景之间进行筛选!当然,几乎每个应用程序都有很多很多的容器视图.

But in practice, you very rarely segue between scenes! Whereas almost every app has many, many, container views as a matter of course.

如果将"prepareForSegue"命名为"loadingContainerView",那会更有意义.

It would make more sense if "prepareForSegue" was called something like "loadingContainerView".

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

A common situation is: You have a small area on the screen, where you want to show one of a number of different view controllers. For example, one of four widgets.

最简单的方法:只需将四个不同的容器视图都放在相同的区域内.在您的代码中,只需隐藏所有四个,然后打开要显示的那个即可.

The simplest way to do this: just have four different container views all sitting inside the same identical area. In your code, simply hide all four and turn on the one you want visible.

容易

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

Say you have a storyboard file "Map.storyboard", storyboard ID is "MapID", and the storyboard is a view controller for your Map class.

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

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

Have an ordinary UIView in your main scene:

@IBOutlet var dynamicContainerView: UIView!

Apple在此处进行说明做添加动态容器视图

Apple explain here the four things you have to do to add a dynamic container view

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

(按此顺序.)

并删除该容器视图:

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

(也按此顺序.)就是这样.

(Also in that order.) That's it.

(但是请注意,在该示例中,dynamicContainerView只是固定视图.它不会更改或调整大小.这假定您的应用程序永不旋转或发生任何其他变化.通常,您必须添加四个常用约束来简单地调整大小后,将map.view保留在dynamicContainerView内.)

(Note however in that example, the dynamicContainerView is simply a fixed view. It does not change or resize. This assumes your app never rotates or anything else. Usually, you would have to add the four usual constraints to simply keep the map.view inside dynamicContainerView, as it resizes.)

这篇关于如何在Objective-C中添加具有自己的UIViewController的子视图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆