关键词:
【中文标题】iOS在启动时呈现模态视图控制器而不使用flash【英文标题】:iOS Present modal view controller on startup without flash 【发布时间】:2014-10-14 08:16:16 【问题描述】:我想在首次启动时向用户展示一个教程向导。
有没有办法在应用程序启动时呈现模态UIViewController
,而至少在一毫秒内看不到它背后的rootViewController
?
现在我正在做这样的事情(为了清楚起见省略了首次启动检查):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
// ...
UIStoryboard *storyboard = self.window.rootViewController.storyboard;
TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];
tutorialViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.window makeKeyAndVisible];
[self.window.rootViewController presentViewController:tutorialViewController animated:NO completion:NULL];
没有运气。我试图将[self.window makeKeyAndVisible];
移到[... presentViewController:tutorialViewController ...]
语句之前,但是模态甚至没有出现。
【问题讨论】:
为什么不将 TutorialViewController 设为 RootViewController? @UlasSancak 因为我希望在用户完成教程后以模态方式将其关闭(最后一个屏幕有一个Let's Start
按钮)。
这些答案有帮助吗?
@Pandara 的回答解决了主要问题(闪烁),但目前尚无模态 VC 的解决方案
【参考方案1】:
所有 presentViewController 方法都需要先出现呈现视图控制器。为了隐藏根 VC,必须呈现一个覆盖。启动屏幕可以继续在窗口上显示,直到显示完成,然后淡出覆盖。
UIView* overlayView = [[[UINib nibWithNibName:@"LaunchScreen" bundle:nil] instantiateWithOwner:nil options:nil] firstObject];
overlayView.frame = self.window.rootViewController.view.bounds;
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIStoryboard *storyboard = self.window.rootViewController.storyboard;
TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];
tutorialViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.window makeKeyAndVisible];
[self.window addSubview:overlayView];
[self.window.rootViewController presentViewController:tutorialViewController animated:NO completion:^
NSLog(@"displaying");
[UIView animateWithDuration:0.5 animations:^
overlayView.alpha = 0;
completion:^(BOOL finished)
[overlayView removeFromSuperview];
];
];
【讨论】:
如果有人面临开始/结束外观转换的不平衡调用,请参阅 Spoek 答案以获取使用DispatchQueue
的解决方案。
我认为 Spoek 把他的名字改成了 ullstrm,反正这是解决方案 Cœur 指的是:***.com/a/41469734/84783 【参考方案2】:
布鲁斯在 Swift 3 中的赞成答案:
if let vc = window?.rootViewController?.storyboard?.instantiateViewController(withIdentifier: "LOGIN")
let launch = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!
launch.view.frame = vc.view.bounds
launch.view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
window?.makeKeyAndVisible()
window?.addSubview(launch.view)
//Using DispatchQueue to prevent "Unbalanced calls to begin/end appearance transitions"
DispatchQueue.global().async
// Bounce back to the main thread to update the UI
DispatchQueue.main.async
self.window?.rootViewController?.present(vc, animated: false, completion:
UIView.animate(withDuration: 0.5, animations:
launch.view.alpha = 0
, completion: (_) in
launch.view.removeFromSuperview()
)
)
【讨论】:
迄今为止最好的解决方案。您甚至可以添加解释为什么必须使用DispatchQueue.global().async
来解决开始/结束外观转换的不平衡调用。
在某些设备上,这似乎偶尔会导致我在启动屏幕上无休止地挂起。其他人有这个问题吗?
似乎是一个“试图在视图上呈现不在视图层次结构中的视图”问题,我试图通过在切换到主线程时添加延迟来解决这个问题,尽管我可以更漂亮,并将其设置为等到主视图控制器完全加载。
我在 iOS 13 beta 6 上遇到了这个问题。我设置了launch .modalPresentationStyle = .fullScreen
,但window?.addSubview(launch.view)
导致演示不是全屏,而是全屏和卡片演示之间出现了一些奇怪的混合.还有其他人在 iOS 13 上遇到问题吗?
好的,似乎使用启动 VC 的第二个实例并将那个视图添加到窗口而不是同一个实例修复了 iOS 13 上的问题。与此示例不同,我使用的是视图要提交的 vc 的数量。【参考方案3】:
也许你可以使用“childViewController”
UIStoryboard *storyboard = self.window.rootViewController.storyboard;
TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];
[self.window addSubview: tutorialViewController.view];
[self.window.rootViewController addChildViewController: tutorialViewController];
[self.window makeKeyAndVisible];
当您需要解散您的导师时,您可以从超级视图中删除其视图。您还可以通过设置 alpha 属性在视图上添加一些动画。希望有帮助:)
【讨论】:
我只是在尝试这种方法。我不能接受这是正确的答案,因为它不使用模态演示,但我赞成它,谢谢! 哈哈哈,你可以参考一些关于“childViewController”的文档,这是一种管理视图的有效方法。做一件事没有唯一正确的方法,对吧?;) 是的,但是对于在这里寻找解决方案的人来说,恕我直言,这不是正确的。无论如何我都会使用这种方法,因为它解决了根本问题:) 在我看来这是正确的解决方案,因为它不能使用 modalviewcontroller :-)【参考方案4】:这个问题在 iOS 10 中仍然存在。我的解决方法是:
-
在
viewWillAppear
中将模态VC 作为childVC 添加到rootVC
在viewDidAppear
:
-
将 modalVC 作为 rootVC 的子项移除
以模态方式呈现不带动画的 childVC
代码:
extension UIViewController
func embed(childViewController: UIViewController)
childViewController.willMove(toParentViewController: self)
view.addSubview(childViewController.view)
childViewController.view.frame = view.bounds
childViewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addChildViewController(childViewController)
func unembed(childViewController: UIViewController)
assert(childViewController.parent == self)
childViewController.willMove(toParentViewController: nil)
childViewController.view.removeFromSuperview()
childViewController.removeFromParentViewController()
class ViewController: UIViewController
let modalViewController = UIViewController()
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
//BUG FIX: We have to embed the VC rather than modally presenting it because:
// - Modal presentation within viewWillAppear(animated: false) is not allowed
// - Modal presentation within viewDidAppear(animated: false) is not visually glitchy
//The VC is presented modally in viewDidAppear:
if self.shouldPresentModalVC
embed(childViewController: modalViewController)
//...
override func viewDidAppear(_ animated: Bool)
super.viewDidAppear(animated)
//BUG FIX: Move the embedded VC to be a modal VC as is expected. See viewWillAppear
if modalViewController.parent == self
unembed(childViewController: modalViewController)
present(modalViewController, animated: false, completion: nil)
//....
【讨论】:
这是一个不错的替代解决方案。但是由于我们正在处理启动问题,因此处理变通方法不应该是 viewController 的责任。这就是为什么我更喜欢 Spoek 的解决方案,它完全在 AppDelegate 中完成。 注意:Spoek 名称现在是 ullstrm @Cœur 这个问题不一定只发生在启动时;当视图控制器第一次出现时,它可能会在视图控制器想要在其自身之上呈现模态视图时发生。该解决方案解决了一般情况,并且比破解 AppDelegate 的启动序列更好。如果显示模式的决定是由视图控制器做出的,那么 AppDelegate 与它有任何关系是不合适的。 @devios1 啊,我不知道这个问题可能发生在启动之外。我想我需要尝试一下。但我不能给出更多的分数:两年前我已经对这个答案投了赞成票。【参考方案5】:可能是一个糟糕的解决方案,但您可以制作一个包含 2 个容器的 ViewController,其中两个容器都链接到一个 VC。然后你可以控制哪个容器应该在代码中可见,这是一个想法
if (!firstRun)
// Show normal page
normalContainer.hidden = NO;
firstRunContainer.hidden = YES;
else if (firstRun)
// Show first run page or something similar
normalContainer.hidden = YES;
firstRunContainer.hidden = NO;
【讨论】:
【参考方案6】:Bruce 的回答为我指明了正确的方向,但是因为我的模式可能会比启动时更频繁地出现(这是一个登录屏幕,所以如果他们注销就需要出现),我不想绑定我的叠加层直接到视图控制器的呈现。
这是我想出的逻辑:
self.window.rootViewController = _tabBarController;
[self.window makeKeyAndVisible];
WSILaunchImageView *launchImage = [WSILaunchImageView new];
[self.window addSubview:launchImage];
[UIView animateWithDuration:0.1f
delay:0.5f
options:0
animations:^
launchImage.alpha = 0.0f;
completion:^(BOOL finished)
[launchImage removeFromSuperview];
];
在不同的部分中,我执行了以典型的self.window.rootViewController presentViewController:...
格式呈现我的登录 VC 的逻辑,无论它是应用启动还是其他方式,我都可以使用它。
如果有人关心,我是这样创建叠加视图的:
@implementation WSILaunchImageView
- (instancetype)init
self = [super initWithFrame:[UIScreen mainScreen].bounds];
if (self)
self.image = WSILaunchImage();
return self;
这是启动图像本身的逻辑:
UIImage * WSILaunchImage()
static UIImage *launchImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
if (WSIEnvironmentDeviceHas480hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-700"];
else if (WSIEnvironmentDeviceHas568hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-700-568h"];
else if (WSIEnvironmentDeviceHas667hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-800-667h"];
else if (WSIEnvironmentDeviceHas736hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-800-Portrait-736h"];
);
return launchImage;
Aaaa 只是为了完成,下面是那些 EnvironmentDevice 方法的样子:
static CGSize const kIPhone4Size = (CGSize).width = 320.0f, .height = 480.0f;
BOOL WSIEnvironmentDeviceHas480hScreen(void)
static BOOL result = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
result = CGSizeEqualToSize([UIScreen mainScreen].bounds.size, kIPhone4Size);
);
return result;
【讨论】:
【参考方案7】:let vc = UIViewController()
vc.modalPresentationStyle = .custom
vc.transitioningDelegate = noFlashTransitionDelegate
present(vc, animated: false, completion: nil)
class NoFlashTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?
if source.view.window == nil,
let overlayViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController(),
let overlay = overlayViewController.view
source.view.addSubview(overlay)
UIView.animate(withDuration: 0, animations: ) (finished) in
overlay.removeFromSuperview()
return nil
【讨论】:
你能提供一些上下文吗?【参考方案8】:可能会迟到,但在您的 AppDelegate 中您可以这样做:
//Set your rootViewController
self.window.rootViewController=myRootViewController;
//Hide the rootViewController to avoid the flash
self.window.rootViewController.view.hidden=YES;
//Display the window
[self.window makeKeyAndVisible];
if(shouldPresentModal)
//Present your modal controller
UIViewController *lc_viewController = [UIViewController new];
UINavigationController *lc_navigationController = [[UINavigationController alloc] initWithRootViewController:lc_viewController];
[self.window.rootViewController presentViewController:lc_navigationController animated:NO completion:^
//Display the rootViewController to show your modal
self.window.rootViewController.view.hidden=NO;
];
else
//Otherwise display the rootViewController
self.window.rootViewController.view.hidden=NO;
【讨论】:
这仍然会在演示发生时导致一些闪烁。【参考方案9】:这就是我使用故事板的方式,它适用于多种模式。 此示例有 3 个。底部、中间和顶部。
请确保在界面生成器中正确设置每个 viewController 的 storyboardID。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
BottomViewController *bottomViewController = [storyboard instantiateViewControllerWithIdentifier:@"BottomViewController"];
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[window setRootViewController:bottomViewController];
[window makeKeyAndVisible];
if (!_loggedIn)
MiddleViewController *middleViewController = [storyboard instantiateViewControllerWithIdentifier:@"middleViewController"];
TopViewController *topViewController = [storyboard instantiateViewControllerWithIdentifier:@"topViewController"];
[bottomViewController presentViewController:middleViewController animated:NO completion:nil];
[middleViewController presentViewController:topViewController animated:NO completion:nil];
else
// setup as you normally would.
self.window = window;
return YES;
【讨论】:
谢谢,不过这样短闪还是会出现 我在这段代码中根本看不到 rootviewcontroller,即使加载了 3 个控制器也是如此。尽管我使用的控制器并没有太多的功能。也许您一次尝试加载太多?会不会是您看到的启动屏幕? 无论我尝试在根视图控制器中加载什么,有时都会出现。sometimes
不适合我:(
您可能需要查看自定义过渡。首先加载顶视图控制器,并使用自定义过渡/转场创建类似于模态解雇的动画。关闭模态呈现的视图控制器时iOS黑屏
】关闭模态呈现的视图控制器时iOS黑屏【英文标题】:iOSBlackscreenwhenclosemodallypresentedviewcontroller【发布时间】:2021-02-1712:13:19【问题描述】:我有一个标签栏控制器,假设用户转到第三个标签并在第三个视图控制器中,用户按下... 查看详情
应用程序激活时呈现模态视图
...【问题描述】:我想在我的应用启动时展示一个模态视图控制器(用于登录屏幕),以及在用户点击主页按钮然后重新启动应用后它再次变为活动状态时。我首先尝试在根视图控制器的viewDidAppear:方法中呈现模态视图。这在应用... 查看详情
横向 IOS 应用程序在呈现模态视图控制器时抛出异常
】横向IOS应用程序在呈现模态视图控制器时抛出异常【英文标题】:LandscapeIOSappthrowsexceptionwhenpresentingmodalviewController【发布时间】:2015-02-1218:19:48【问题描述】:作为参考,我正在使用Xcode6.1并针对IOS7.1。看起来有类似问题的帖... 查看详情
如何使用 iOS 7 自定义转换在顶部呈现半模态视图控制器
】如何使用iOS7自定义转换在顶部呈现半模态视图控制器【英文标题】:HowtopresentahalfmodalviewcontrolleroverthetopwithiOS7customtransitions【发布时间】:2014-04-0500:34:44【问题描述】:我将如何在主视图控制器的顶部显示一个“半视图”控制... 查看详情
UIActivity activityViewController 在 iPad 上以模态方式呈现,而不是在弹出窗口中
...iOS6中使用客户UIActivity子类时,可以指定一个自定义视图控制器,当您从初始UIAc 查看详情
为啥在呈现这个模态视图时会有延迟?
...:2011-02-1000:42:39【问题描述】:我有一个以加载多个导航控制器的tableview(来自xib)开头的应用程序。如果正在进行长时间的初始化序列,我想在启动时显示模式视图。我尝试在AppDelegate中呈现模式视图,但直到底层代码已经完 查看详情
在呈现模态视图时显示 UITabBar
...】:当用户点击UITabBar项目时,我想以模态方式呈现视图控制器,但我也希望UITabBar保持可见。当用户完成模态视图控制器时,我想以模态方式关闭它。基本上,我想在另一个视图控制器上显示一个视图控制器,并使用模态动画... 查看详情
使用故事板从应用程序委托呈现模态视图控制器
】使用故事板从应用程序委托呈现模态视图控制器【英文标题】:presentingmodalviewcontrollerfromappdelegatewithstoryboards【发布时间】:2012-03-1314:18:29【问题描述】:如果我对我的iOS项目中的一件事感到遗憾,那就是我从一开始就开始使... 查看详情
当视图控制器从 nib 文件加载时,以模态方式在当前上下文中呈现视图控制器
】当视图控制器从nib文件加载时,以模态方式在当前上下文中呈现视图控制器【英文标题】:Presentaviewcontrollermodallyovercurrentcontextwhentheviewcontrollergetsloadfromanibfile【发布时间】:2019-10-2515:37:51【问题描述】:我需要展示从xib文件... 查看详情
在 iOS 10 中以模态方式呈现新 VC 时崩溃
...遇到了一个奇怪的崩溃问题;iOS9运行良好。从当前视图控制器模态显示相机视图控制器时,它会崩溃。这是崩溃的代码行:-(void)searchBarBookmarkButtonClicked:(UISearchBar*)sea 查看详情
IOS/Objective-C:在呈现视图控制器中设置属性
】IOS/Objective-C:在呈现视图控制器中设置属性【英文标题】:IOS/Objective-C:Setpropertyinpresentingviewcontroller【发布时间】:2016-11-1901:13:02【问题描述】:当您使用代码创建模态视图控制器时,模态控制器可以访问呈现控制器。请阅读... 查看详情
当视图控制器以模态方式呈现时,用户可以滚动
】当视图控制器以模态方式呈现时,用户可以滚动【英文标题】:Usercanscrollwhenviewcontrollerispresentedmodally【发布时间】:2016-04-1814:45:27【问题描述】:在我的应用中,有一个包含5个视图控制器的大滚动视图。有4个视图控制器以模... 查看详情
在键盘处于活动状态时呈现模态视图控制器
】在键盘处于活动状态时呈现模态视图控制器【英文标题】:presentingamodalviewcontrollerwhilethekeyboardisactive【发布时间】:2014-08-0503:21:46【问题描述】:所以我基本上有一个表单,由几个文本字段组成。用户像往常一样输入字段。但... 查看详情
模态视图控制器优于呈现视图控制器
】模态视图控制器优于呈现视图控制器【英文标题】:ModalviewcontrolleroverPresentingviewcontroller【发布时间】:2014-12-2118:46:10【问题描述】:我有一个视图控制器,我以模态方式呈现在另一个视图控制器上,顶部VC的背景视图具有模... 查看详情
呈现模态视图控制器时 iPhone 崩溃
】呈现模态视图控制器时iPhone崩溃【英文标题】:iPhonecrashingwhenpresentingmodalviewcontroller【发布时间】:2010-11-2713:48:35【问题描述】:我试图在另一个视图以模态方式呈现后立即显示一个模态视图(第二个是出现的加载视图)。-(vo... 查看详情
为啥一个简单的模态视图控制器在呈现和关闭时会滞后?
】为啥一个简单的模态视图控制器在呈现和关闭时会滞后?【英文标题】:Whywouldasimplemodalviewcontrollerlagwhenpresentedanddismissed?为什么一个简单的模态视图控制器在呈现和关闭时会滞后?【发布时间】:2013-06-0317:50:55【问题描述】:... 查看详情
在启动时加载并呈现第二个视图控制器
】在启动时加载并呈现第二个视图控制器【英文标题】:LoadandPresentasecondviewcontrolleratlaunch【发布时间】:2018-01-2516:20:02【问题描述】:我试图在启动时在第一个视图控制器之上呈现第二个视图控制器,而不关闭第一个。这个帖... 查看详情
“viewWillTransitionToSize:”当视图控制器以模态方式呈现时在 iOS 9 中未调用
】“viewWillTransitionToSize:”当视图控制器以模态方式呈现时在iOS9中未调用【英文标题】:"viewWillTransitionToSize:"notcallediniOS9whentheviewcontrollerispresentedmodally【发布时间】:2015-10-0109:34:04【问题描述】:我展示了另一个视图控制... 查看详情