我正在为拥有自己的UIViewControllers
的子视图而苦苦挣扎。我有UIViewController
的视图(浅粉色)和toolbar
上的两个按钮。我希望在按下第一个按钮时显示蓝色视图,按下第二个按钮时显示的黄色视图。如果我只想显示一个视图,应该很容易。但蓝色视图将包含一个表,因此它需要它自己的控制器。那是我的第一课。我从this SO question开始,在那里我学会了我需要一个桌面控制器。
所以,我要支持并采取一些婴儿步骤。下面是我的Utility ViewController
(主视图控制器)和另外两个控制器(蓝色和黄色)的简单起点图片。想象一下,当首次显示Utility ViewController
(主视图)时,将显示粉红色视图所在的蓝色(默认)视图。用户可以单击两个按钮来回移动,粉红色视图将永远不会显示。我只想将蓝色视图移到粉红色视图的位置,将黄色视图移到粉红色视图所在的位置。我希望这是有道理的。
我试图使用addChildViewController
。根据我的看法,有两种方法可以做到这一点:storyboard
或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
-------------------------- EDIT ----------------- -------------
self.aboutVC.aboutView为零。但是我在storyboard
中连接了它。我还需要实例化吗?
答案 0 :(得分:128)
此帖可追溯到现代iOS的早期,通常具有最新的语法,例如目前的Swift 4。如果您从iOS,自动布局等开始,它将帮助您。
今天的iOS &#34;一切都是容器视图&#34; 。这是您今天制作应用程序的基本方式。
应用程序可能非常简单,只有一个视图。但即使在这种情况下,每个&#34;&#34;在屏幕上是一个容器视图。
这很容易......
将容器视图拖到场景视图中。 (就像你会拖入UIButton一样。)
容器视图是此图像中的棕色内容。它实际上是内部您的场景视图。
当您将容器视图拖到场景视图中时,Xcode会自动为您提供 两件事 :
您在场景视图中获得容器视图 ,
你得到一个全新的UIViewController
只是坐在故事板白色的某个地方 。
这两个 已连接 与&#34;共济会符号&#34;事情 - 在下面解释!
真的那么简单。
您已完成。
以下是视觉上解释的相同内容。
请注意(A)
处的 容器视图 。
请注意(B)
处的 控制器 。
点击B.(那是B - 不是A!)
转到右上方的检查员。注意它说&#34; UIViewController&#34;
[] [3]
将其更改为您自己的自定义类,即UIViewController。
所以,我有一个Swift类Snap
,它是UIViewController
。
所以说它&#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;事情就是塞古。
点击 &#34; masonic symbol&#34;选择segue。的事情。
向右看。
您必须键入segue的文本标识符。
您决定姓名。 它可以是任何文本字符串。明智的选择通常是&#34; segueClassName&#34;。
如果您遵循该模式,您的所有segue将被称为segueClockView,seguePersonSelector,segueSnap,segueCards等。
接下来,您在哪里使用该文本标识符?
然后,在代码中,在整个场景的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
调用连接变量以引用控制器。
说你&#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模式中的类似结果,您必须使用协议和委托,记住该委托将是一个弱链接。)
值得注意的是&#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];
}