我想在初次启动时向用户提供一个教程向导。
有没有办法在应用程序启动时显示模态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 ...]
语句之前,但之后模态甚至都没有出现。
答案 0 :(得分:31)
所有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];
}];
}];
答案 1 :(得分:10)
布鲁斯在斯威夫特3中提出的回答:
createRandomPerson()
答案 2 :(得分:8)
可能是您可以使用“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属性在视图上添加一些动画。希望有用:)
答案 3 :(得分:6)
这个问题在iOS 10中仍然存在。我的修复是:
viewWillAppear
中将modal VC作为childVC添加到rootVC viewDidAppear
中的代码:
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)
}
//....
}
}
答案 4 :(得分:0)
可能是一个糟糕的解决方案,但你可以制作一个包含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;
}
答案 5 :(得分:0)
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;
}
Aaaaand只是为了完成,这里是那些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;
}
答案 6 :(得分:0)
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
}
}
答案 7 :(得分:0)
可能要晚了,但是在您的AppDelegate中,您可以这样做:
browser.get("https://google.com/")
def beta_check_href(self):
elem = self.find_elements_by_xpath("//a[@href]")
for el in elem:
print elem.get_attribute("href")
答案 8 :(得分:-1)
这是我使用故事板的方式,它适用于多种模态。 这个例子有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;
}