モーダルで、最初の起動時にチュートリアルウィザードをユーザーに提示します。
アプリケーションの起動時にモーダル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 ...]
ステートメントですが、モーダルは表示されません。
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];
}];
}];
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()
})
})
}
}
}
「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プロパティを設定して、ビューにアニメーションを追加することもできます。
この問題はまだiOS 10に存在します。私の修正は:
viewWillAppear
にモーダルVCをchildVCとしてルートVCに追加します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)
}
//....
}
}
悪い解決策かもしれませんが、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;
}
ブルースの答えは私を正しい方向に向けましたが、私のモーダルは起動時よりも頻繁に表示される可能性があるため(ログイン画面であるため、ログアウトした場合は表示する必要があります)、オーバーレイを直接に接続したくありませんでしたビューコントローラのプレゼンテーション。
これが私が思いついた論理です:
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];
}];
別のセクションでは、ログインを提示するロジックを実行しますVC典型的なself.window.rootViewController presentViewController:...
フォーマット。アプリの起動かどうかに関係なく使用できます。
誰かが気になっている場合は、オーバーレイビューを作成する方法を次に示します。
@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;
}
遅くなる可能性がありますが、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;
}
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
}
}