IOS - Como segue programaticamente usando swift

185

Estou criando um aplicativo que usa o SDK do Facebook para autenticar usuários. Estou tentando consolidar a lógica do facebook em uma classe separada. Aqui está o código (simplificado):

import Foundation

class FBManager {
    class func fbSessionStateChane(fbSession:FBSession!, fbSessionState:FBSessionState, error:NSError?){
        //... handling all session states
        FBRequestConnection.startForMeWithCompletionHandler { (conn: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in

            println("Logged in user: \n\(result)");

            let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
            let loggedInView: UserViewController = storyboard.instantiateViewControllerWithIdentifier("loggedInView") as UserViewController

            loggedInView.result = result;

            //todo: segue to the next view???
        }
    }
}

Estou usando o método de classe acima para verificar as alterações do estado da sessão e funciona bem.

P: Depois de obter os dados do usuário, como posso seguir para a próxima exibição nessa classe personalizada?

EDIT: para deixar claro, eu tenho um segue com identificador no storyboard e estou tentando encontrar uma maneira de executar um segue de uma classe que não é o controlador de exibição

Shlomi Schwartz
fonte
2
Gosta performSegue:?
TEM
Sim, mas o código não está no viewController, como posso obter isso?
Shlomi Schwartz
Bem, nesse caso, você deve delegar esse trabalho (a seguir) do objeto em que realiza o trabalho no controlador de exibição (por meio de um bloco de conclusão ou de um método de delegação).
TEM
recebendo uma exceção de layout nulo #
Chris Hayes

Respostas:

340

Se seus seguimentos existem no storyboard com um identificador de seguimentos entre suas duas visualizações, você pode chamá-lo de forma programática usando:

performSegue(withIdentifier: "mySegueID", sender: nil)

Para versões mais antigas:

performSegueWithIdentifier("mySegueID", sender: nil)

Você também pode fazer:

presentViewController(nextViewController, animated: true, completion: nil)

Ou se você estiver em um controlador de Navegação:

self.navigationController?.pushViewController(nextViewController, animated: true)
Jérôme
fonte
7
Obrigado pela resposta, como posso chamar performSegueWithIdentifier de uma classe personalizada que não é a visualização?
Shlomi Schwartz
2
Se eu usar o seu segundo método. Como passar dados para a próxima visualização?
Iamprem 12/04
2
Você usaria func prepareForSegue (segue: UIStoryboardSegue, remetente: AnyObject?) E definiria as propriedades.
Lloyd Sargent
1
Observe que, ao chamar performSequeWithIdentifer (), uma implementação de prepareForSeque () no seu controlador de chamada será chamada e você poderá fazer vários dados passando para lá - na verdade, o parâmetro sender recebido em prepareForSeque () é simplesmente o que é passado ao parâmetro sender performSequeWithIdentifier ()
timbo 4/11/15
1
E se o segue não existir no storyboard? Por exemplo, se você deseja criar uma segue para o próprio ViewController?
scaryguy
20

Você pode usar o NSNotification

Adicione um método de postagem em sua classe personalizada:

NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)

Adicione um observador ao seu ViewController:

NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOFReceivedNotication:", name:"NotificationIdentifier", object: nil)

Adicione função no seu ViewController:

func methodOFReceivedNotication(notification: NSNotification){
    self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)
}
Rodrigo Otero
fonte
1
Por favor, não abuse de notificações como esta. Use um delegado e torne explícita a conexão entre o controlador de exibição e sua classe. É quase impossível observar o fluxo de controle de uma notificação, porque pode haver vários assinantes e as instâncias podem se inscrever / cancelar a inscrição à vontade.
uliwitness
17

Se seus seguimentos existem no storyboard com um identificador de seguimentos entre suas duas visualizações, você pode chamá-lo programaticamente usando

self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)

Se você estiver no controlador de navegação

let viewController = YourViewController(nibName: "YourViewController", bundle: nil)        
self.navigationController?.pushViewController(viewController, animated: true)

Vou recomendar você para uma segunda abordagem usando o controlador de navegação.

Manish Mahajan
fonte
self.performSegue (withIdentifier: "yourIdentifierInStoryboard", remetente: self)
Rajneesh071
14

Você pode usar segue assim:

self.performSegueWithIdentifier("push", sender: self)
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if segue.identifier == "push" {

    }
}
Rudra
fonte
Obrigado pela resposta, no entanto performSegueWithIdentifier é um método viewController, e meu código é executado em uma classe externa
Shlomi Schwartz
13

Swift 3 - Também funciona com o SpriteKit

Você pode usar o NSNotification .

Exemplo:

1.) Crie um segue no storyboard e nomeie o identificador "segue"

2.) Crie uma função no ViewController a partir do qual você está seguindo.

func goToDifferentView() {

    self.performSegue(withIdentifier: "segue", sender: self)

}

3.) No ViewDidLoad () do seu ViewController, você cria o observador.

NotificationCenter.default.addObserver(self, selector: #selector(goToDifferentView), name: "segue" as NSNotification.Name, object: nil)

Atualização - A última vez que usei isso, tive que alterar a .addObserverchamada para o código a seguir para silenciar os erros.

NotificationCenter.default.addObserver(self, selector: #selector(goToDifferentView), name: NSNotification.Name(rawValue: "segue"), object: nil)

4.) No ViewController ou Scene em que você está seguindo, adicione o método Post sempre que desejar que o seguimento seja acionado.

NotificationCenter.default.post(name: "segue" as NSNotification.Name, object: nil)

Atualização - A última vez que usei isso, tive que alterar a .postchamada para o código a seguir para silenciar os erros.

NotificationCenter.default.post(NSNotification(name: NSNotification.Name(rawValue: "segue"), object: nil) as Notification)
Timmy Sorensen
fonte
Swift 3:NotificationCenter.default.addObserver(self, selector: #selector(goToDifferentView), name: NSNotification.Name(rawValue: "segue"), object: nil)
Michael Samoylov 21/09
3

O que você quer fazer é realmente importante para o teste de unidade. Basicamente, você precisa criar uma pequena função local no controlador de exibição. Nomeie a função como qualquer coisa, apenas inclua o performSegueWithIndentifier.

func localFunc() {
    println("we asked you to do it")
    performSegueWithIdentifier("doIt", sender: self)
}

Em seguida, altere sua classe de utilitário FBManagerpara incluir um inicializador que aceite um argumento de uma função e uma variável para manter a função do ViewController que executa o seguinte.

public class UtilClass {

    var yourFunction : () -> ()

    init (someFunction: () -> ()) {
        self.yourFunction = someFunction
        println("initialized UtilClass")
    }

    public convenience init() {
        func dummyLog () -> () {
            println("no action passed")
        }
        self.init(dummyLog)
    }

    public func doThatThing() -> () {
        // the facebook login function
        println("now execute passed function")
        self.yourFunction()
        println("did that thing")
    }
}

(O init de conveniência permite que você use isso em testes de unidade sem executar as instruções.)

Finalmente, onde você tem // todo: segue para a próxima visualização ???, coloque algo como:

self.yourFunction()

Nos testes de unidade, você pode simplesmente invocá-lo como:

let f = UtilClass()
f.doThatThing()

onde doThatThing é sua mudança de fbsession e UtilClassé FBManager.

Para o seu código real, basta passar localFunc(sem parênteses) para a classe FBManager.

Conta
fonte
2

Isso funcionou para mim.

Primeiro, forneça ao controlador de exibição em seu storyboard um Storyboard ID dentro do inspetor de identidade. Em seguida, use o seguinte código de exemplo (garantindo que a classe, o nome do storyboard e o ID do storyboard correspondam aos que você está usando):

let viewController:
UIViewController = UIStoryboard(
    name: "Main", bundle: nil
).instantiateViewControllerWithIdentifier("ViewController") as UIViewController
// .instantiatViewControllerWithIdentifier() returns AnyObject!
// this must be downcast to utilize it

self.presentViewController(viewController, animated: false, completion: nil)

Para obter mais detalhes, consulte http://sketchytech.blogspot.com/2012/11/instantiate-view-controller-using.html cumprimentos

amdan
fonte
1

Você pode fazer isso usando a performSegueWithIdentifierfunção

Captura de tela

Sintaxe:

func performSegueWithIdentifier(identifier: String, sender: AnyObject?)

Exemplo:

 performSegueWithIdentifier("homeScreenVC", sender: nil)
Jayprakash Dubey
fonte
1

Outra opção é usar segue modal

PASSO 1: Vá para o storyboard e dê ao View Controller um Storyboard ID . Você pode encontrar onde alterar a ID do storyboard no Identity Inspector, à direita. Vamos chamar o ID do storyboardModalViewController

PASSO 2: Abra o controlador de exibição 'remetente' (vamos chamá-lo ViewController) e adicione este código a ele

public class ViewController {
  override func viewDidLoad() {
    showModalView()
  }

  func showModalView() {
    if let mvc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ModalViewController") as? ModalViewController {
      self.present(mvc, animated: true, completion: nil)
    }
  }
}

Observe que o View Controller que queremos abrir também é chamado ModalViewController

PASSO 3: Para fechar o ModalViewController, adicione-o

public class ModalViewController {
   @IBAction func closeThisViewController(_ sender: Any?) {
      self.presentingViewController?.dismiss(animated: true, completion: nil)
   }
}
Aphoe
fonte
0

Isso funcionou para mim:

//Button method example
 @IBAction func LogOutPressed(_ sender: UIBarButtonItem) {

        do {
            try Auth.auth().signOut()
            navigationController?.popToRootViewController(animated: true)

        } catch let signOutError as NSError {
          print ("Error signing out: %@", signOutError)
        }


    }

fonte