domingo, 2 de janeiro de 2011

Criando apps para iPhone com Swift - Interface de usuário



É claro que eu sei sobre a sua ansiedade em aprender Swift, a linguagem de programação que vamos usar, mas ainda há algumas coisas importantes sobre interface de usuário, as quais você precisa aprender antes de “mergulhar” no estudo da linguagem Swift em si.

Vou acrescentando as lições nos próximos dias

Acompanhe o material do curso e veja o resto das lições!

Como é a interface de usuário de uma app iOS



Eu nem vou perder tempo ensinando a criar interfaces antigas, baseadas em “xlb”, pois certamente você não as utilizará mais.


Para começar a falar sobre UI (User Interface ou Interface do Usuário), temos que conceituar como é uma UI em uma app iOS.


Uma “scene” ou “cena” (doravante chamaremos simplesmente de “scene”), é o que aparece em uma “tela” de sua app. Dependendo do tipo de dispositivo iOS, podemos ter uma ou mais scenes por tela física. Para dispositivos menores, uma scene é uma tela (iPhone e iPod).


Uma “window” ou “janela” (doravante chamaremos de “window”), é um “container” onde você pode acomodar “views” ou “visões” (doravente chamaremos de “view”). Uma view é um pedaço da window, no qual você quer colocar um conteúdo.


As views podem conter outras views, em um relacionamento de hierarquia. Vamos ver o Storyboard da app que fizemos no capítulo passado (“MeuPrimeiroTeste.storyboard”):



Na figura, vemos o Storyboard aberto, com o “Document Outline” aparecendo à esquerda (lembra-se como abrir o “Document Outline”? É só clicar no botão que fica no canto inferior direito do Editor de Storyboard).


A window não aparece, mas pode crer que ela está lá! Temos uma scene (“View Controller Scene”), que contém uma view (está selecionada). Essa é a view principal, na qual todas as outras foram criadas. Pode-se dizer que ela é a “mãe” das outras views.


Logo abaixo da view, temos as views que nós criamos:
  • “Seu nome” : Um Label;
  • “Rounded Style Text Field” : Um Text Field;
  • “Olá” : Um Button;
  • “” (em Branco) : O Label de saída, onde jogamos a mensagem.


Ao clicar sobre uma view da scene, ela será selecionada no editor automaticamente.



Uma scene é uma window, com views dentro. E uma view pode conter outras views.


Cada view que existe em nosso Storyboard é uma instância de UIView ou de uma classe derivada dela. Se você está acostumado com outras plataformas, como Android, por exemplo, pode estar se perguntando: “Como eu identifico as instâncias das views?”


No iOS você não identifica as instâncias da mesma maneira que em outra plataformas. O relacionamento entre uma view e um ViewController é feito através de “outlets”. O atributo “@IBOutlet”, que já usamos, permite associar uma view a uma variável.


Porém, existe a propriedade “tag”, que você pode alterar para identificar as views:




Basta selecionar a view, ir para a área de utilities, selecionar o “Attributes Inspector” (o quarto ícone da esquerda para a direita), rolar até as propriedades da view e modificar o valor do “tag”. Agora, você pode usar a propriedade “tag” dentro do código, para saber se é a view que você deseja usar.



O ViewController

O ViewController é onde você controla suas views programaticamente, modificando seu estado de acordo com o modelo (MVC). Em outras palavras, você pode:
  • Obter o texto ou estado de uma view;
  • Mudar o texto ou estado de uma view;
  • Executar ações baseadas em eventos gerados por uma view;



Então, eu tenho 1 ViewController por app?


Até pode fazer isso, mas o certo é ter um ViewController por scene.


Quando criamos uma conexão “outlet” para uma view, temos acesso às propriedades dessa view. Quando criamos uma conexão “action” para uma view, interceptamos eventos gerados por essa view.


Vamos ver o nosso ViewController, do exemplo anterior, em maior detalhe:



import UIKit

class ViewController: UIViewController {

  @IBOutlet var txtNome : UITextField!
  @IBOutlet var lblMsg : UILabel!
  
  @IBAction func digaOla(sender : AnyObject) {
    lblMsg.text = "Bom dia, " + txtNome.text
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }


}


Para começar, ele importa a biblioteca UIKit, que é o framework para criação de interfaces de usuário no iOS. A instrução “import” funciona de maneira semelhante ao “import”, do Java, ou seja, torna todos os elementos de um framework disponíveis para uso. Por que? Porque vamos usar elementos de UI, como: UIViewController etc.

A seguir, vem a criação de uma classe Swift chamada: “ViewController”, que descende de outra classe: “UIViewController”. A nossa classe tem 2 propriedades (“txtNome” e “lblMsg”) e 3 métodos (“digaOla()”, “viewDidLoad()” e “didReceiveMemoryWarning()”).

Como vimos, as duas propriedades estão conectadas à duas views em nosso Storyboard (do qual esse ViewController faz parte). E essa conexão é feita em duas partes: Pelo atributo “@IBOutlet” e pela associação manual que fizemos no Interfece Builder (arrastando o “outlet” para a view).

E os métodos? Eles são invocados externamente:
  • “digaOla()” é uma conexão “@IBAction”, e foi associado ao Botão “Olá”. Quando ocorrer o evento “TouchUpInside”, esse método será invocado;
  • “viewDidLoad()” é um método de UIViewController, que você pode sobrescrever (fazer “override”), para inicializar suas views. Ele é chamado assim que toda a hierarquia da sua scene for carregada;
  • “didReceiveMemoryWarning()” é chamado quando o sistema iOS está com pouca memória, logo, você deve tentar liberar memória, desalocando estruturas.

Hora de uma nova aplicação

Definitivamente, é hora de fazermos mais uma app. Se você já viajou para os Estados Unidos, certamente teve problemas para converter algumas medidas que eles usam, certo? Por exemplo? Quantos galões de gasolina deve colocar? O que significa “high eighties” em temperatura Fahreinheit?

Esta app está completa, dentro do código-fonte do curso, na pasta “capt3/Conversor.zip”. Mas eu sugiro fortemente que você tente fazer alguma coisa.

Então, vamos criar uma app com mais de uma scene e mais de um ViewController. Nossa app vai ter duas “telas”:

Uma tela para converter temperaturas de graus Fahreinheit (“F”) para graus Celsius (“C”). Temos um certo intervalo de temperaturas “F” (de -20 F a 150 F) e, ao selecionarmos uma delas, a temperatura em graus Celsius é mostrada na lista de baixo. E vice-versa.

A outra tela é para conversão de volume, de Galões para Litros:


Ao digitar um volume em galões, e tocar no botão “Para litros”, você terá o valor convertido e colocado no Text Field de baixo. E vice-versa.


Internacionalização


Apesar de pequena, queremos que nossa app funcione em Português do Brasil, e, mais do que isso: Queremos que os números apareçam com o formato Brasileiro, com vírgula como separador de decimais. Então, temos que configurar o idioma e o local do nosso iOS Simulator.

Na primeira vez em que você roda o programa, ele ainda estará configurado para Inglês / Estados Unidos. Então, nós vamos mudar isso. Com o simulador em primeiro plano, selecione o menu “Hardware / Home”, para simular um toque no botão Home do dispositivo (aquele que fica na parte inferior, na frente). Você verá a tela inicial, igual a um dispositivo Apple:
Como pode notar, esse simulador tem 2 telas. Isso pode ser constatado pelos dois pequenos círculos na parte de baixo, logo acima do ícone do Navegador Safari. Temos que acessar a app “Settings”. Então, temos que rolar para a primeira tela. Dependendo do dispositivo de controle que você usar em seu Mac, a operação é:
  • Mouse: Clique, segure e arraste para a direita até a tela mudar;
  • Touchpad: Toque com um dedo e clique, segure e, com outro dedo, arraste para a direita a té a tela mudar;
Aí é como todo dispositivo iOS:
  1. Abra o app “Settings”;
  2. Toque em “General”;
  3. Toque em “Language & Region”;
  4. Toque em “iPhone Language” e selecione “Português (Brasil)” (a rolagem da tela é feita do mesmo modo que você usou para achar a app “Settings”);
  5. Volte para “Language & Region”, toque em “Região” (deve estar em Português), e selecione “Brazil”;
Deve ficar assim:  

Como será a UI dessa aplicação

Podemos começar vendo o Storyboard dessa app é assim:



Esta é uma app do tipo “Tabbed Application”. Tem 3 scenes: Uma com o Tab Bar Controller, e outras duas scenes com os nossos Tab Bar Item. Calma, vou explicar!


Tab bar é um tipo de controle que permite navegar rapidamente entre funcionalidades diferentes de uma app. Ele fica em uma scene própria, com o seu próprio ViewControler: UITabBarController. Em outras palavras, não codificamos o ViewController. Porém, podemos adicionar scenes que são navegáveis através da Tab Bar, que contém Tab Bar Item. Note na figura do Storyboard que temos uma scene inicial, com uma barra na parte inferior e dois itens. Esse é o Tab Bar.


Da scene inicial, partem duas referências para as duas outras scenes, que são navegadas a partir do tab bar desta scene.


Quando a app é iniciada, o primeiro item do Tab Bar é navegado automaticamente, e sua scene é renderizada. Se você clicar em outro item na Tab Bar, a outra scene será mostrada.


Também vamos ver outras coisas, como:
  • Internacionalização: Como formatar números de acordo com a configuração do usuário (locale);
  • Como usar vetores;
  • Como usar PickerView;


Criando o projeto e formatando as scenes

Ok. Crie um novo projeto no Xcode, e, ao selecionar o template (gabarito), escolha: “Tabbed Application”.


Abra o arquvo “.storyboard” no Xcode e você verá as 3 scenes. Lembre-se de ativar o “Document Outline”.


Abra o “Document Outline” e certifique-se que você tenha essa estrutura:



Temos dois itens em nossa Tab Bar e duas scenes, mas não queremos esses nomes: “First” e “Second”. Queremos renomear tudo para: “Temperatura” e “Volume”. Para renomear os itens de tab bar, selecione a view “First”, selecione o controle Tab Bar Item nela e, no Attributes Inspector, troque seu atributo “Title” para “Temperatura”.



Agora, selecione a segunda scene (“Second”), selecione o Tab Bar Item, e troque seu nome para: “Volume”.

Agora, se você verificar no “Document Outline”, verá que os nomes das scenes e dos itens de Tab Bar foram alterados. Pode ser bobagem, mas os nomes dos ViewControllers ainda estão como: “First” e “Second”. Podemos mudar isso facilmente, bastando selecionar cada ViewController e, no Atributes Inspector mudar o “title” de cada um deles para: “Temperatura” e “Volume”. Assim, você ficará com um “Document Outline” parecido com esse:




Agora, vamos criar a aparência da view “Temperatura”. Para isso, arraste dois labels e dois “Picker Views” para ela:



O Picker View é interessante, por que é como uma roleta: Você gira e ele para em um determnado item. Pode até girar forte! Por default, os Picker Views ficam vazios quando executamos o programa, logo, teremos que preenchê-los com dados.


Mas, antes de mais nada, vamos rodar nossa app e ver como está ficando.

Tem algo errado... Tudo bem que os dois Picker View estejam vazios, pois nós já sabemos a razão, mas por que os dois Labels estão posicionados no canto direito? Nós os colocamos no meio?

A razão é que não definimos as restrições de auto-layout, que fazem com que os elementos se reposicionem de acordo com o tamanho da tela. Vamos fazer de uma forma simples e mais adiante veremos mais detalhes. Selecione a view e clique no terceiro icone do canto inferior direito do Editor (“Resolve Auto Layout Issues”):


Vai aparecer um menu de contexto, então, selecione a opção “Add missing constraints” e pronto! Pode rodar novamente que tudo estará centralizado certinho.


Agora, crie a segunda view (“Volume”) e faça a mesma coisa:


Bem, agora é hora de criar as conexões “outlet” para:
  • Temperatura:
    • Picker View de Fahreinheit;
    • Picker View de Celsius;
  • Volumer:
    • Text Field de Galões;
    • Text Field de Litros;

Eis as “outlets” a serem criadas no “FirstViewController.swift”, correspondentes à scene de Temperatura:


class FirstViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {

  @IBOutlet var celsiusPicker: UIPickerView!
  @IBOutlet var fahreinheitPicker: UIPickerView!



E, no “SecondViewController.swift”, vamos criar as “outlets” correspondentes à scene de Volume:


class SecondViewController: UIViewController {

  @IBOutlet var txtGaloes: UITextField!
  @IBOutlet var txtLitros: UITextField!


Agora, ligue esses “outlets” às views correspondentes, da mesma maneira que fizemos no exercício do capítulo anterior. Você tem que praticar isso!


No caso das duas Picker Views, nós não vamos criar conexões “action”. Vamos usar outro mecanismo: Delegate. Porém, no caso dos dois botões da scene Volume, temos que associar os botões às actions.

Mexendo no código

Antes de continuar, eu recomendaria uma pausa, uns 20 minutos, para você absorver o que leu até agora. Vem algumas “esquisitices” do Swift pela frente, e você precisa estar bem relaxado(a), para entender a visão geral.

Ok, vamos começar pelo ViewController da scene de Temperatura: “FirstViewController.swift”. Vamos acertar a tela toda e só depois veremos a outra scene.

Para começar, vou mostrar o algoritmo que eu usei. Pode não ser a melhor solução, mas, pelo menos, me deixou mostrar como o Picker View funciona. Eu criei dois vetores, um para Fahreinheit e outro para Celsius. Depois, fui “empurrando” temperaturas “F” para dentro do primeiro vetor, e depois convertendo para Celsius e “empurrando” para o segundo. Desta forma, os dois Picker Views serão sincronizados pelo número da linha.

Então, vou criar dois vetores como propriedades da classe View Controller:

var fahrTemps : [String] = []
var cTemps : [String] = []

Em Swift, declaramos vetores dessa forma, com o tipo de dados entre chaves. E também estamos inicializando os dois como vetores vazios.

Agora, vamos mexer no método “viewDidLoad()”, pois temos que inicializar os dois vetores e, consequentemente, os dois Picker Views:


  override func viewDidLoad() {
    super.viewDidLoad()
    var quoc : Double = 5.0 / 9.0
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .DecimalStyle
    formatter.minimumFractionDigits = 2
    formatter.maximumFractionDigits = 2
    for x in -20...150 {
      var tf :Double  = Double(x)
      fahrTemps.append( formatter.stringFromNumber(tf)! + " F")
      var tc : Double = (tf - 32) * quoc
      cTemps.append(formatter.stringFromNumber(tc)! + " C")
      celsiusPicker.dataSource = self
      celsiusPicker.delegate = self
      celsiusPicker.tag = 2
      fahreinheitPicker.dataSource = self
      fahreinheitPicker.delegate = self
      fahreinheitPicker.tag = 1
    }
  }


Para começar, a fórmula para converter Fahreinheit em Celsius é:

C = (F – 32) * 5/9

Eu vou encher o vetor de Fahreinheit e, para cada valor, calculo o Celsius e coloco nele, na mesma posição.

Ambas as Picker Views só aceitam Strings como elementos de suas listas. Então, depois de calcular as temperaturas, temos que convertê-las para Strings. Como estamos no Brasil, e usamos vírgula como separador decimal, precisamos usar o NSNumberFormatter para converter números reais em Strings, respeitando a configuração de idioma do usuário.

Instanciamos um NSNumberFormatter com o comando:

let formatter = NSNumberFormatter()

O comando “let” é semelhante ao “var”, só que serve para criar constantes. Uma vez que uma variável tenha sido instanciada com “let”, ela não pode apontar para outro objeto. No Swift não usamos o comando “new” para instanciar. Basta usar o nome da classe seguido de parêntesis.

Depois, mudamos algumas propriedades da nossa instância de NSNumberFormatter:
  • “formatter.numberStyle = .DecimalStyle" : Especifica que o formato de separador de decimais da “locale” do dispositivo deverá ser utilizado;
  • “formatter.minimumFractionDigits = 2" : Queremos no mínimo 2 casas decimais;
  • “formatter.maximumFractionDigits = 2”: Queremos no máximo 2 casa decimais.

Depois, fazemos um loop de -20 até 150 fraus “F”, para montar o vetor de Fahreinheits, convertendo cada temperatura para String e armazenando. Depois, fazemos o mesmo para o vetor de graus Celsius.

Agora, vamos explicar o resto do método (após o “loop”) linha a linha:
  • “celsiusPicker.dataSource = self ” : Estamos apontando a fonte de dados para a Picker View de temperaturas Celsius, para a nossa própria classe. Ou seja, nossa classe será chamada para preencher a Picker View;
  • “celsiusPicker.delegate = self ” : Estamos interceptando os eventos da Picker View de Celsius em nossa classe;
  • “celsiusPicker.tag = 2 ” : Estamos atribuindo um “tag” à Picker View de Celsius, para poder identificá-la mais adiante;
  • “fahreinheitPicker.dataSource = self ” : Estamos apondando a fonte de dados da Picker View de Fahreinheit para a nossa própria classe;
  • “fahreinheitPicker.delegate = self ” : Estamos interceptando os eventos dessa Picker View em nossa própria classe;
  • “fahreinheitPicker.tag = 1 ” : Estamos atribuindo um “tag” a esta Picker View.



Ok. Muita coisa, não? Sem dúvida!


No iOS, temos uma coisa chamada “Protocol” (“Protocolo”). Um protocolo é como uma interface da linguagem Java: Um conjunto de métodos que uma classe deve implementar, para ser aderente a ele. Quando declaramos que nossa classe é Datasource e Delegate de Picker Views, temos que acrescentar os dois protocolos à ela:
  • “UIPickerViewDataSource” : Temos que implementar os métodos: “numberOfComponentsInPickerView” e “pickerView(_ pickerView: UIPickerView,numberOfRowsInComponent component: Int) -> Int”;
  • “UIPickerViewDelegate” : Temos que implementar os métodos: “pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!” e “pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)”;
O Swift aceita uma sintaxe muito estranha para nomes de métodos, baseada em “parâmetros nomeados”, então, relaxe que depois falaremos disso.



Continuando, temos que mudar a definição da classe do nosso ViewController, acrescentando os dois novos Protocolos que vamos implementar: “UIPickerViewDataSource” e “UIPickerViewDelegate”:

class FirstViewController: UIViewController,UIPickerViewDataSource,UIPickerViewDelegate {



Agora, vamos acrescentar os métodos que faltam:



func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {

}



func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {

}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

}



No primeiro método “numberOfComponentsInPickerView”, temos que retornar o número de componentes que nossa Picker View tem. Um Picker View pode mostrar mais de uma “coluna”, sendo que cada “coluna” é um componente. No nosso caso, retornaremos 1 porque só temos uma única “coluna”, com o valor da temperatura:



func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}

No método “pickerView:numberOfRowsInComponent”, temos que retornar a quantidade de linhas que o componente da nossa Picker View tem. É só retornar o tamanho de um dos dois vetores que criamos:



func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return cTemps.count
}

No método “pickerView:titleForRow”, temos que retornar o título de uma determinada linha do componente da Picker View. Esse método será chamado e vamos receber o número da linha cujo título devemos informar. Se estiver sendo chamado para a Picker View de Celsius, o que podemos saber pelo “tag”, mandamos uma ocorrência do vetor cTemps, caso contrário, mandamos uma ocorrência do vetor fahrTemps. É por isso que diferenciamos as Picker Vies por “tags”, lembra? Eis o método:


  func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    if pickerView.tag == 2 {
      return cTemps[row]
    }
    else {
      return fahrTemps[row]
    }
  }


Finalmente, no método “pickerView:didSelectRow”, temos que dizer o que deve ser feito, quando o usuário selecionar uma linha em uma das duas Picker Views. No nosso caso, vamos sincronizar a outra. Se ele selecionar uma linha em Fahreinheit, vamos reposicionar a Picker View de Celsius, e vice versa. Mais uma vez, temos que identificar qual Picker View ele selecionou usando o “tag”:


  func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    if pickerView.tag == 2 {
      fahreinheitPicker.selectRow(row, inComponent: 0, animated: true)
    }
    else {
      celsiusPicker.selectRow(row, inComponent: 0, animated: true)
    }
  }


Uma parte já foi!


Seu programa deve estar funcionando agora. Pelo menos para a scene de Temperatura. Tente selecionar 80 F e veja se aparece 26,67 C:


Ok. Se funcionou, ótimo! Se não funcionou, veja o exemplo nos arquivos do livro: “capt3/Conversor.zip”.

Agora, vamos para o Volume

Na scene de Volume, será mais simples, pois vamos usar apenas Text Fields e Botões.

Antes de começarmos, temos que delimitar o tipo de valor que o usuário pode teclar em cada Text Field. Abra o “Main.storyboard”, selecione o primeiro Text Field da scene de Volume, e, no Attributes inspector mude a propriedade “Keyboard type” para “Decimal pad”. Assim, só os números e o separador decimal aparecerão no soft keyboard do dispositivo. Faça o mesmo para o segundo Text Field.

O segundo ViewController (“SecondViewController”) é muito simples:


Ok. Se funcionou, ótimo! Se não funcionou, veja o exemplo nos arquivos do livro: “capt3/Conversor.zip”.

Agora, vamos para o Volume

Na scene de Volume, será mais simples, pois vamos usar apenas Text Fields e Botões. 

Antes de começarmos, temos que delimitar o tipo de valor que o usuário pode teclar em cada Text Field. Abra o “Main.storyboard”, selecione o primeiro Text Field da scene de Volume, e, no Attributes inspector mude a propriedade “Keyboard type” para “Decimal pad”. Assim, só os números e o separador decimal aparecerão no soft keyboard do dispositivo. Faça o mesmo para o segundo Text Field. 

O segundo ViewController (“SecondViewController”) é muito simples: 

import UIKit

class SecondViewController: UIViewController {

  @IBOutlet var txtGaloes: UITextField!
  @IBOutlet var txtLitros: UITextField!
  
  @IBAction func paraLitros(sender: AnyObject) {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .DecimalStyle
    formatter.minimumFractionDigits = 2
    formatter.maximumFractionDigits = 2
    var gallons : Double = Double(formatter.numberFromString(txtGaloes.text)!)
    var litros : Double = gallons * 3.78541178
    txtLitros.text = formatter.stringFromNumber(litros)
  }
  
  @IBAction func paraGaloes(sender: AnyObject) {
    let formatter = NSNumberFormatter()
    formatter.numberStyle = .DecimalStyle
    formatter.minimumFractionDigits = 2
    formatter.maximumFractionDigits = 2
    var litros : Double = Double(formatter.numberFromString(txtLitros.text)!)
    var gallons : Double = litros * 0.264172052
    txtGaloes.text = formatter.stringFromNumber(gallons)
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }


}


Nós só criamos as partes em negrito. Duas conexões “outlet” e duas “actions”, uma para cada botão.


Se o usuário digitar um valor em “Galões” e clicar no botão “Para Litros”, o método “paraLitros” será invocado. Usamos um NSNumberFormatter, da mesma forma que fizemos na scene de Temperatura. Só que aqui também vamos usar o NSNumberFormatter para transformar o valor digitado pelo usuário, que é String, em um número real. Se não fizermos isso, o iOS vai se confundir todo com a vírgula decimal:


var gallons : Double = Double(formatter.numberFromString(txtGaloes.text)!)


A priopriedade “text”, de um Text Field, contém o String que o usuário digitou. Estamos usando o método “numberFromString”, do NSNumberFormatter, para gerar um objeto NSNumber. Depois, fazemos um “cast”, transformando-o em um valor do tipo Double.



O resto, deve ser trivial.



Testando tudo



Bom, agora, você pode tentar rodar as duas scenes da aplicação, convertendo temperaturas e valores.


Como adicionar outras scenes a um Tab Bar Controller



Neste exemplo, só temos duas scenes: Temperatura e Volume. E se quiséssemos uma scene que convertesse distâncias, digamos de milhas para quilômetros?


Você precisa de uma nova scene. E esta scene tem que ter um ViewController e uma View. Só que o ViewController tem duas partes: O código Swift e sua representação na hierarquia de views do seu Storyboard.


Para começar, temos que criar uma classe ViewController, para nossa nova scene. Selecione o seu projeto no Project Navigator, e acione o menu “File / New / File...”:



Selecione “Cocoa Touch Class”, para abrir o assistente de classes do Framework do iOS: 


Dê um nome significativo para sua Classe, e faça com que ela seja uma subclasse de “UIViewController”. Salve-a na pasta do projeto, junto com as outras classes.


Agora, que você tem um novo ViewController, pode criar a parte visual. Edite o “Main.storyboard”, selecione um objeto “View Controller”, na “Object Library”, e arraste para o seu Storyboard. Coloque-o embaixo da scene de Volume:




O Objeto View Controller vem com sua própria window e view principal. Só que precisamos apontar para a nossa classe recém-criada (“ThirdViewController”). Com o seu Objeto View Controller selecionado no Storyboard, verifique o Identity Inspector. Note que a classe está como “UIViewController”. Precisamos mudar para a nossa classe recém-criada:


Mude o atributo “Class”, no Identity Inspector, para “ThirdViewController” (tem que ser a classe que você criou no projeto Swift). Pode selecionar clicando na seta à esquerda.


Lembre-se de salvar tudo: “File / Save”.

Agora, temos um ViewControler, associado à nossa classe Swift, mas que está “solto”. Precisamos ligá-lo ao Tab Bar Controller! Vamos fazer 2 coisas: Acrescentar um Tab Bar Item e depois conectá-lo ao nosso Tab Bar Controller.

Acrescente um elemento “Tab Bar Item” ao seu novo objeto ViewController, e mude seu título para “Distância”. Se quiser, pode mudar o ícone também. Selecione o Tab Bar Item e, no Attributes Inspector (você já sabe onde é) selecine uma “System Image”. Eu mudei para “Featured”. Mais adiante, mostraremos como criar ícones para Tab Bar.




Para conectar o Objeto Visual View Controller ao Tab Bar Controller, o procedimento é:
  1. No Editor do Storyboard, posicione de uma maneira que dê para ver a tela do Tab Bar Controller e a tela do seu novo Storyboard;
  2. Pressionando a tecla “Control”, clique dentro da área livre da tela do Tab Bar Controller e arraste para dentro da área livre da tela do seu novo View Controller. Ao fazer isso, uma linha azul aparecerá indicando o caminho, como na próxima figura.

Ao levantar o dedo, aparecerá um menu de contexto para você selecionar o tipo de relacionamento que quer criar. Selecione “View Controllers”, dentro de “Relationship segue”. Agora, uma linha aparecerá ligando permanentemente o seu View Controller ao Tab Bar Controller. Isso pode ser visto no “Document Outline”:




Agora, tente rodar a app e veja que o Tab Bar apresenta 3 itens. Tente navegar para o último.

Desta vez, eu não farei com você! Eis a tela do novo View Controller:



Eis os “outlets” e “actions” que você deve criar em sua nova classe de ViewController:

  


import UIKit

class ThirdViewController: UIViewController {

  @IBOutlet var txtMilhas: UITextField!
  @IBOutlet var txtPes: UITextField!
  @IBOutlet var txtMetros: UITextField!
  
  @IBAction func toqueMilhas(sender: AnyObject) {
  }
  
  @IBAction func toquePes(sender: AnyObject) {
  }
  
  @IBAction func toqueParaMilhas(sender: AnyObject) {
  }
  
  
  @IBAction func toqueParaPes(sender: AnyObject) {
  }
  

Ok, então, como fazer as conversões? Use um mecanismo parecido com o que fizemos para a tela de Volume, incluindo o NSNumberFormatter. As fórmulas são:
  • Milhas para Metros: Multiplique o valor em milhas por 1.609,344;
  • Pés para Metros: Multiplique o valor em pés por 0,3048;
  • Metros para Milhas: Multiplique o valor em metros por 0,00062137;
  • Metros para Pés: Multiplique o valor em metros por 3,2808399;

Lembre-se que, para colocar esses valores no código, você deve retirar o ponto separador de milhares e trocar a vírgula pelo ponto decimal.

Alguns valores para teste:




Milhas
Pés
Metros
1
5.280
1.609,344
5
26.400
8.046,72
15
79.200
24.140,16



Conclusão

Neste grande e cansativo capítulo, você viu muita coisa. Vamos recordar:
  • O que é hierarquia de view;
  • O que são: scene, view e ViewController;
  • Como criar uma app com mais de uma scene;
  • Como internacionalizar apps (pelo menos a parte numérica);
  • Como adicionar novas telas a uma app e ligá-las a um Tab Bar Controller.

E você viu um pouco mais da linguagem Swift também. Agora, é o momento de dar uma “paradinha” e estudar um pouco mais de Swift, usando um “Playground”.

Não se esqueça!

Acesse a página do curso para ver as outras lições, e sempre baixe novamente o zip do curso, pois, como é um trabalho em andamento, pode haver correções de erros e aprimoramentos.

Se tiver dúvidas, use o fórum!

Esse "curso" não dá diploma algum! E todo o material é liberado sob licença "Creative Commons" compartilha igual.

Você pode compartilhar esse material da forma que desejar, desde que mantenha o mesmo tipo de licença e as atribuições de autoria original.


O trabalho "Criando apps para iPhone com Swift " de Cleuton Sampaio de Melo Jr está licenciado com uma Licença Creative Commons - Atribuição-CompartilhaIgual 4.0 Internacional. Isso inclui: Textos, páginas, gráficos e código-fonte.