ios compatibilité - Définissez par programme le contrôleur de vue initial à l'aide de Storyboards




définition nouveautés (15)

Comment est-ce que je programme par programme l' InitialViewController pour un Storyboard? Je souhaite ouvrir mon storyboard à une vue différente en fonction de certaines conditions qui peuvent varier d'un lancement à l'autre.


Answers

let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "storyBoardid") as! ViewController
let navigationController = UINavigationController(rootViewController: vc)
UIApplication.shared.delegate.window?.rootViewController = navigationController

Une autre façon est de présenter viewController,

let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "storyBoardid") as! ViewController
self.present(vc,animated:true,completion:nil)

D'abord vous devez créer l'objet de votre storyboard puis changer de racine (si nécessaire) puis vous prenez référence du contrôleur de vue particulier qui est poussé contrôleur de vue courante (si vous changez de racine) sinon c'est présent nouveau contrôleur de vue


Solution simple et facile à trouver - inutile de supprimer le "contrôle initial du contrôleur de vue" du storyboard et de modifier l'onglet Info du projet et d'utiliser makeKeyAndVisible , juste

self.window.rootViewController = rootVC;

dans

- (BOOL) application:didFinishLaunchingWithOptions:

Une autre solution avec l'utilisation de Swift 3 et Swift 4 avec éviter la coulée de force est comme ceci

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    guard let viewController = storyboard.instantiateViewController(withIdentifier: "YourViewController") as? YourViewController else {
        return false
    }
    self.window?.rootViewController = viewController
    self.window?.makeKeyAndVisible()
    return true
}

Et ci-dessous utilise avec UINavigationController

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    self.window = UIWindow(frame: UIScreen.main.bounds)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    guard let viewController = storyboard.instantiateViewController(withIdentifier: "YourViewController") as? YourViewController else {
        return false
    }
    let navigationController = UINavigationController(rootViewController: viewController)
    self.window?.rootViewController = navigationController
    self.window?.makeKeyAndVisible()
    return true
}

Comment faire sans un contrôleur de vue initiale factice

Assurez-vous que tous les contrôleurs de vue initiaux possèdent un ID Storyboard.

Dans le storyboard, désélectionnez l'attribut "Is initial View Controller" du premier contrôleur de vue.

Si vous exécutez votre application à ce stade, vous lirez:

Failed to instantiate the default view controller for UIMainStoryboardFile 'MainStoryboard' - perhaps the designated entry point is not set?

Et vous remarquerez que votre propriété de fenêtre dans le délégué de l'application est maintenant nulle.

Dans le cadre de l'application, accédez à votre cible et à l'onglet Info . Il est clair la valeur du Main storyboard file base name . Sous l'onglet General , effacez la valeur de l' Main Interface . Cela supprimera l'avertissement.

Créez la fenêtre et le contrôleur de vue initial souhaité dans l'application du délégué de l' application:didFinishLaunchingWithOptions: méthode:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];

    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

    UIViewController *viewController = // determine the initial view controller here and instantiate it with [storyboard instantiateViewControllerWithIdentifier:<storyboard id>];

    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];

    return YES;
}

Vous pouvez définir par programme le rootViewController de la fenêtre clé dans l' (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions

par exemple:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if (shouldShowAnotherViewControllerAsRoot) {
        UIStoryboard *storyboard = self.window.rootViewController.storyboard;
        UIViewController *rootViewController = [storyboard instantiateViewControllerWithIdentifier:@"rootNavigationController"];
        self.window.rootViewController = rootViewController;
        [self.window makeKeyAndVisible];
    }

    return YES;
}

Il y a quelques jours, j'ai rencontré la même situation. Une astuce très simple a résolu ce problème. J'ai mis mon contrôleur de vue initial caché avant le lancement2. Si le contrôleur de vue initial est le bon contrôleur, il est visible dans viewDidLoad. Sinon, une séquence est exécutée sur le contrôleur de vue souhaité. Cela fonctionne parfaitement dans iOS 6.1 et au-dessus. Je suis sûr que cela fonctionne sur les versions antérieures d'iOS.


J'ai créé une classe de routage pour gérer la navigation dynamique et garder une classe AppDelegate propre, j'espère qu'elle aidera aussi les autres.

//
//  Routing.swift
// 
//
//  Created by Varun Naharia on 02/02/17.
//  Copyright © 2017 TechNaharia. All rights reserved.
//

import Foundation
import UIKit
import CoreLocation

class Routing {

    class func decideInitialViewController(window:UIWindow){
        let userDefaults = UserDefaults.standard
        if((Routing.getUserDefault("isFirstRun")) == nil)
        {
            Routing.setAnimatedAsInitialViewContoller(window: window)
        }
        else if((userDefaults.object(forKey: "User")) != nil)
        {
            Routing.setHomeAsInitialViewContoller(window: window)
        }
        else
        {
            Routing.setLoginAsInitialViewContoller(window: window)
        }

    }

    class func setAnimatedAsInitialViewContoller(window:UIWindow) {
        Routing.setUserDefault("Yes", KeyToSave: "isFirstRun")
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let animatedViewController: AnimatedViewController = mainStoryboard.instantiateViewController(withIdentifier: "AnimatedViewController") as! AnimatedViewController

        window.rootViewController = animatedViewController
        window.makeKeyAndVisible()
    }

    class func setHomeAsInitialViewContoller(window:UIWindow) {
        let userDefaults = UserDefaults.standard
        let decoded  = userDefaults.object(forKey: "User") as! Data
        User.currentUser = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! User

        if(User.currentUser.userId != nil && User.currentUser.userId != "")
        {
            let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
            let homeViewController: HomeViewController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
            let loginViewController: UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "LoginNavigationViewController") as! UINavigationController
            loginViewController.viewControllers.append(homeViewController)
            window.rootViewController = loginViewController
        }
        window.makeKeyAndVisible()
    }

    class func setLoginAsInitialViewContoller(window:UIWindow) {
        let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let loginViewController: UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "LoginNavigationViewController") as! UINavigationController

        window.rootViewController = loginViewController
        window.makeKeyAndVisible()
    }

  class func setUserDefault(_ ObjectToSave : Any?  , KeyToSave : String)
    {
        let defaults = UserDefaults.standard

        if (ObjectToSave != nil)
        {

            defaults.set(ObjectToSave, forKey: KeyToSave)
        }

        UserDefaults.standard.synchronize()
    }

    class func getUserDefault(_ KeyToReturnValye : String) -> Any?
    {
        let defaults = UserDefaults.standard

        if let name = defaults.value(forKey: KeyToReturnValye)
        {
            return name as Any
        }
        return nil
    }

    class func removetUserDefault(_ KeyToRemove : String)
    {
        let defaults = UserDefaults.standard
        defaults.removeObject(forKey: KeyToRemove)
        UserDefaults.standard.synchronize()
    }

}

Et dans votre appel AppDelegate cette

 self.window = UIWindow(frame: UIScreen.main.bounds)
 Routing.decideInitialViewController(window: self.window!)

Swift 4, Xcode 9

dans le fichier AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let firstVC = storyboard.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
    self.window?.rootViewController = firstVC
}

Dans AppDelegate.swift vous pouvez ajouter le code suivant:

let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "YourViewController_StorboardID")
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()

Bien sûr, vous devez implémenter votre logique, en fonction des critères que vous choisirez pour un contrôleur de vue approprié.

Aussi, n'oubliez pas d'ajouter une identité (sélectionnez storyboard -> Controller Scene -> Afficher l'inspecteur d'identité -> assigner StorboardID).


Sélectionnez le contrôleur de vue que vous souhaitez ouvrir en premier et accédez à l'inspecteur d'attributs. Aller à la scène initiale et vérifier est l'option du contrôleur de vue initiale.

Maintenant, ce sera votre contrôleur de vue initial qui s'ouvrira en premier lors du lancement de l'application.


Je ne pense pas que ce soit possible. Au lieu de cela, vous pouvez avoir un contrôleur initial qui aura des segues à différents contrôleurs de vue. Au démarrage, vous pouvez décider quel segment effectuer par programmation.


Pour tous les amoureux de Swift , voici la réponse de @Travis traduite en SWIFT :

Faites ce que @Travis a expliqué avant le code Objective C. Alors,

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    var exampleViewController: ExampleViewController = mainStoryboard.instantiateViewControllerWithIdentifier("ExampleController") as! ExampleViewController

    self.window?.rootViewController = exampleViewController

    self.window?.makeKeyAndVisible()

    return true
}

Le ExampleViewController est le nouveau contrôleur de vue initial que vous souhaitez afficher.

Les étapes expliquées:

  1. Créer une nouvelle fenêtre avec la taille de la fenêtre actuelle et la définir comme notre fenêtre principale
  2. Instancier un storyboard que nous pouvons utiliser pour créer notre nouveau contrôleur de vue initial
  3. Instancier notre nouveau contrôleur de vue initial basé sur son ID Storyboard
  4. Réglez le contrôleur de vue racine de notre nouvelle fenêtre en tant que notre nouveau contrôleur que nous venons d'initier
  5. Rendre notre nouvelle fenêtre visible

Profitez et bonne programmation!


Ouvrez le tableau principal, sélectionnez l'affichage que vous voulez démarrer en premier, puis ouvrez Utilitaires -> Attributs. En dessous du "View Controller", vous voyez le bouton radio "Is initial View Controller". Il suffit de le sélectionner.

--- A la question révisée:

Peut-être pouvez-vous essayer ceci: écrivez une méthode dans la section ViewDidLoad de votre vue initial et lorsque la méthode s'exécute au lancement de l'application, la méthode déclenche une segmentation vers une autre vue.


Vous pouvez définir rootviewcontroller de navigation en tant que contrôleur de vue principale. Cette idée peut être utilisée pour la connexion automatique selon les exigences de l'application.

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];

UIViewController viewController = (HomeController*)[mainStoryboard instantiateViewControllerWithIdentifier: @"HomeController"];

UINavigationController navController = [[UINavigationController alloc] initWithRootViewController:viewController];

 self.window.rootViewController = navController;

if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {

    // do stuff for iOS 7 and newer

    navController.navigationBar.barTintColor = [UIColor colorWithRed:88/255.0 green:164/255.0 blue:73/255.0 alpha:1.0];

    navController.navigationItem.leftBarButtonItem.tintColor = [UIColor colorWithRed:88/255.0 green:164/255.0 blue:73/255.0 alpha:1.0];

    navController.navigationBar.tintColor = [UIColor whiteColor];

    navController.navigationItem.titleView.tintColor = [UIColor whiteColor];

    NSDictionary *titleAttributes [email protected]{

                                     NSFontAttributeName :[UIFont fontWithName:@"Helvetica-Bold" size:14.0],

                                     NSForegroundColorAttributeName : [UIColor whiteColor]

                                     };

    navController.navigationBar.titleTextAttributes = titleAttributes;

    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

}

else {

    // do stuff for older versions than iOS 7

    navController.navigationBar.tintColor = [UIColor colorWithRed:88/255.0 green:164/255.0 blue:73/255.0 alpha:1.0];



    navController.navigationItem.titleView.tintColor = [UIColor whiteColor];

}

[self.window makeKeyAndVisible];

Pour les utilisateurs de StoryboardSegue

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];

// Go to Login Screen of story board with Identifier name : LoginViewController_Identifier

LoginViewController *loginViewController = (LoginViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:@“LoginViewController_Identifier”];

navigationController = [[UINavigationController alloc] initWithRootViewController:testViewController];

self.window.rootViewController = navigationController;

[self.window makeKeyAndVisible];

// Go To Main screen if you are already Logged In Just check your saving credential here

if([SavedpreferenceForLogin] > 0){
    [loginViewController performSegueWithIdentifier:@"mainview_action" sender:nil];
}

Merci


Si je comprends bien, vous voulez:

  1. Concevoir une cellule dans IB qui peut être utilisée dans plusieurs scènes de storyboard.
  2. Configurez des séquences de storyboard uniques à partir de cette cellule, en fonction de la scène dans laquelle se trouve la cellule.

Malheureusement, il n'y a actuellement aucun moyen de le faire. Pour comprendre pourquoi vos tentatives précédentes n'ont pas fonctionné, vous devez en savoir plus sur le fonctionnement des storyboards et des cellules prototype. (Si vous ne vous souciez pas de savoir pourquoi ces autres tentatives n'ont pas fonctionné, n'hésitez pas à partir maintenant.J'ai pas de solutions magiques pour vous, autre que de suggérer que vous déposez un bug.)

Un storyboard n'est, en substance, pas beaucoup plus qu'une collection de fichiers .xib. Lorsque vous chargez un contrôleur de vue de table contenant des prototypes de storyboard, voici ce qui se passe:

  • Chaque cellule prototype est en fait sa propre mini-plume intégrée. Ainsi, lorsque le contrôleur de vue de table se charge, il parcourt chacune des plumes et des appels de la cellule prototype -[UITableView registerNib:forCellReuseIdentifier:] .
  • La vue de la table demande au contrôleur les cellules.
  • Vous appelez probablement -[UITableView dequeueReusableCellWithIdentifier:]
  • Lorsque vous demandez une cellule avec un identifiant de réutilisation donné, elle vérifie si une puce a été enregistrée. Si c'est le cas, il instancie une instance de cette cellule. Ceci est composé des étapes suivantes:

    1. Regardez la classe de la cellule, telle que définie dans la plume de la cellule. Appelez [[CellClass alloc] initWithCoder:] .
    2. La méthode -initWithCoder: passe à travers et ajoute des sous-vues et définit les propriétés qui ont été définies dans la plume. ( IBOutlet est probablement connecté ici, bien que je ne l'ai pas testé, ça peut arriver dans -awakeFromNib )
  • Vous configurez votre cellule comme vous le souhaitez.

La chose importante à noter ici est qu'il existe une distinction entre la classe de la cellule et l' apparence visuelle de la cellule. Vous pouvez créer deux cellules prototypes distinctes de la même classe, mais avec leurs sous-vues présentées différemment. En fait, si vous utilisez les styles UITableViewCell par défaut, c'est exactement ce qui se passe. Le style "Default" et le style "Subtitle", par exemple, sont tous deux représentés par la même classe UITableViewCell .

Ceci est important : la classe de la cellule n'a pas de corrélation bi-univoque avec une hiérarchie de vue particulière. La hiérarchie de vue est entièrement déterminée par ce qui se trouve dans la cellule prototype enregistrée avec ce contrôleur particulier.

Notez également que l'identificateur de réutilisation de la cellule n'a pas été enregistré dans un dispensaire cellulaire mondial. L'identifiant de réutilisation est uniquement utilisé dans le contexte d'une seule instance UITableView .

Compte tenu de cette information, regardons ce qui s'est passé dans vos tentatives ci-dessus.

Dans Controller # 1, ajoutez une cellule prototype, définissez la classe sur ma sous-classe UITableViewCell, définissez l'ID de réutilisation, ajoutez les étiquettes et les connectez aux prises de la classe. Dans le contrôleur n ° 2, ajoutez une cellule prototype vide, définissez-la sur la même classe et réutilisez l'ID comme précédemment. Lorsqu'il s'exécute, les étiquettes n'apparaissent jamais lorsque les cellules sont affichées dans le contrôleur n ° 2. Fonctionne bien dans le contrôleur n ° 1.

C'est prévu. Bien que les deux cellules aient la même classe, la hiérarchie de vue transmise à la cellule dans le contrôleur n ° 2 était entièrement dépourvue de sous-vues. Vous avez donc une cellule vide, ce qui est exactement ce que vous avez mis dans le prototype.

Conçu chaque type de cellule dans un NIB différent et câblé jusqu'à la classe de cellule appropriée. Dans le storyboard, ajoutez une cellule prototype vide et définissez son identifiant de classe et de réutilisation pour faire référence à ma classe de cellule. Dans les méthodes viewDidLoad des contrôleurs, ces fichiers NIB ont été enregistrés pour l'ID de réutilisation. Lorsque montré, les cellules dans les deux contrôleurs étaient vides comme le prototype.

Encore une fois, c'est prévu. L'identifiant de réutilisation n'est pas partagé entre les scènes de storyboard ou les nibs, de sorte que le fait que toutes ces cellules distinctes aient le même identifiant de réutilisation n'a aucun sens. La cellule que vous récupérez à partir de la vue de table aura une apparence qui correspond à la cellule prototype dans cette scène du storyboard.

Cette solution était proche, cependant. Comme vous l'avez noté, vous pouvez simplement appeler par programme -[UITableView registerNib:forCellReuseIdentifier:] , en passant l' UINib contenant la cellule, et vous obtiendrez cette même cellule. (Ce n'est pas parce que le prototype "écrasait" la plume, vous n'aviez simplement pas enregistré la plume avec la tableview, donc il regardait toujours la plume incorporée dans le storyboard.) Malheureusement, il y a une faille dans cette approche - il n'y a aucun moyen de brancher des enchaînements storyboard à une cellule dans une plume autonome.

Les prototypes conservés dans les deux contrôleurs sont vides et définissent la classe et réutilisent l'identifiant dans ma classe de cellule. Construit l'interface utilisateur des cellules entièrement en code. Les cellules fonctionnent parfaitement dans tous les contrôleurs.

Naturellement. J'espère que ce n'est pas surprenant.

Donc, c'est pourquoi ça n'a pas marché. Vous pouvez concevoir vos cellules dans des plumes autonomes et les utiliser dans plusieurs scènes de storyboard; vous ne pouvez pas actuellement connecter des séquences de storyboard à ces cellules. Heureusement, vous avez appris quelque chose en lisant ceci.





ios objective-c swift uiviewcontroller uistoryboard