Tudo o que você queria saber sobre o SwiftUI, mas tinha medo de perguntar


Oi Meu nome é Renat, estou desenvolvendo um serviço de análise de assinaturas no iOS - Apphud.


Como você sabe, a Apple na WWDC 2019 apresentou sua nova estrutura SwiftUI, projetada no futuro para substituir (ou não?) O familiar UIKit. SwiftUI permite que você descreva a interface do aplicativo em um estilo declarativo e reduz bastante a quantidade de código.


A Apple já introduziu alguns tutoriais de inglês interessantes com muitos exemplos. Vou tentar falar sobre a nova estrutura na forma de perguntas e respostas. Então vamos lá.


Antes de começar


Para trabalhar com o SwiftUI, você precisa fazer o download do Xcode 11 Beta . Você também deve ser um desenvolvedor Apple registrado. Ter o mais recente macOS Catalina é desejável, mas não necessário. Sem ele, o Canvas não estará disponível.


Portanto, no Xcode 11 Beta, crie um novo projeto e verifique se “Use SwiftUI” está marcado.


Perguntas e Respostas


Para onde foi o Interface Builder?


O SwiftUI não precisa mais do Interface Builder - ele foi substituído pelo Canvas , um editor de interface interativo que está intimamente relacionado ao código. Ao escrever um código, sua parte visual na tela é gerada automaticamente e vice-versa. Muito conveniente e, o mais importante, seguro. Agora, seu aplicativo não @IBOutlet devido ao fato de você ter esquecido de atualizar a @IBOutlet com a variável Neste artigo, não tocaremos na tela , consideraremos apenas o código.


O lançamento do aplicativo mudou?


Sim, agora o objeto inicial na interface do aplicativo não é UIWindow , mas a nova classe UIScene (ou seu UIWindowScene descendente). E uma janela é adicionada à cena. Essas alterações afetam não apenas o SwiftUI, mas o iOS 13 como um todo.


Ao criar um projeto, você verá os arquivos AppDelegate , SceneDelegate e ContentView . SceneDelegate - um delegado da classe UIWindowScene , usado para controlar cenas no aplicativo. Remanescente do AppDelegate .


A classe SceneDelegate é especificada em Info.plist
A classe SceneDelegate é especificada em Info.plist


Na scene: willConnectTo: options: método delegado scene: willConnectTo: options: cria uma janela e um UIHostingController raiz, que contém um ContentView . ContentView é a nossa página inicial. Todo o desenvolvimento será realizado nesta classe.


Qual a diferença entre o View e o UIView?


ContentView.swift abrir o ContentView.swift , você verá uma declaração de contêiner do ContentView. Como você já entendeu, não existem métodos viewDidLoad ou viewDidAppear familiares viewDidLoad viewDidAppear . A base das telas aqui não é o UIViewController , mas a View . A primeira coisa a notar é que o ContentView é uma struct que aceita o protocolo View . Sim, o View agora se tornou um protocolo e muito simples. O único método que você precisa implementar no ContentView é descrever o body da variável. Todas as suas subvisões e visualizações personalizadas devem aceitar o protocolo View , ou seja, devem ter uma variável de body .


O que é corpo?


Body é diretamente nosso contêiner, onde todas as outras subvisões são adicionadas. Isso é um pouco semelhante ao corpo em uma página html , onde a página html é um ContentView . Body sempre deve ter exatamente um descendente e qualquer classe que aceite o protocolo View .


 struct ContentView: View { var body: some View { Text("Hello, world!") } } 

Tipos de retorno opacos ou o que é algum?


A construção de some TypeName é uma inovação do Swift 5.1 chamada tipo de retorno opaco . É usado para casos em que não é importante para nós qual objeto retornar, o principal é que ele suporta o tipo especificado, neste caso, o protocolo View .


Se simplesmente escrevemos var body: View , isso significa que devemos retornar a View . A classe Any também não funciona, pois teríamos que executar uma operação de conversão de tipo ( usando o as! Operator ). Portanto, eles criaram a palavra especial some na frente do nome do protocolo para indicar o tipo de retorno opaco . Em vez de View podemos retornar Text , Image , VStack - qualquer coisa, pois todos eles suportam o protocolo View . Mas deve haver exatamente um elemento: ao tentar retornar mais de uma View compilador lançará um erro.



Erro de compilação ao tentar retornar mais de um elemento ao corpo


Qual é a sintaxe dentro dos colchetes e onde está o addSubview?


O Swift 5.1 introduziu a capacidade de agrupar objetos em um único todo em um estilo declarativo. Isso é semelhante a uma matriz dentro de um bloco de fechamento , mas os elementos são enumerados de uma nova linha sem vírgulas e retorno . Esse mecanismo foi chamado de Construtor de Funções .


Isso é amplamente usado no SwiftUI. Com base no Function Builder, eles criaram o ViewBuilder - um designer de interface declarativo. Usando o ViewBuilder , não precisamos mais escrever addSubview para cada elemento - basta listar todos os View de uma nova linha dentro do bloco de fechamento . O SwiftUI adicionará e agrupará elementos em um contêiner pai mais complexo.


 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @_functionBuilder public struct ViewBuilder { /// Builds an empty view from an block containing no statements, `{ }`. public static func buildBlock() -> EmptyView /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through /// unmodified. public static func buildBlock<Content>(_ content: Content) -> Content where Content : View } 

Anúncio do ViewBuilder na estrutura SwiftUI


Como adicionar UILabel, UIImageView e outros elementos?


Os elementos são criados de maneira muito simples: cada View precisa ser gravada a partir de uma nova linha e alterar a aparência usando as funções de modificador ( modificadores de exibição ). A diferença entre modificadores e funções familiares para nós é que eles sempre retornam um objeto contêiner em vez de void . Portanto, podemos criar cadeias inteiras de modificadores através do ponto.


 var body: some View { VStack{ Text("World Time").font(.system(size: 30)) Text("Yet another subtitle").font(.system(size: 20)) } } 

No entanto, nem todos os controles e o View têm seus análogos no SwiftUI. Aqui está uma lista parcial de classes do UIKit e seus análogos:


  • UITableView -> List


  • UICollectionView não tem analógico


  • UILabel -> Text


  • UITextField -> TextField


  • UIImageView -> Image


  • UINavigationController -> NavigationView


  • UIButton -> Button


  • UIStackView -> HStack / VStack


  • UISwitch -> Toggle


  • UISlider -> Slider


  • UITextView não tem analógico


  • UIAlertController -> Alert / ActionSheet


  • UISegmentedControl -> SegmentedControl


  • UIStepper -> Stepper


  • UIDatePicker -> DatePicker



Como é a navegação entre as telas?


O controlador de navegação assume o papel de um NavigationView especial. Apenas envolva seu código no NavigationView{} . E a própria ação de transição pode ser adicionada a um botão NavigationLink especial, que pressiona a tela DetailView condicional.


 var body: some View { NavigationView { Text("World Time").font(.system(size: 30)) NavigationLink(destination: DetailView() { Text("Go Detail") } } } 

Como apresentar novas visões modalmente? Isso é feito, por exemplo, usando a construção de planilha :


 Button(action: { print("Button Pushed") self.show_modal = true }) { Text("Present Modal") }.sheet(isPresented: self.$show_modal) { ModalView() } 

Como mencionado acima, o body pode retornar não apenas uma instância do View , mas também qualquer outra classe que aceite este protocolo. Isso nos dá a oportunidade de DetailView não o DetailView , mas também o Text ou a Image !


Como organizar elementos na tela?


Os elementos são organizados independentemente um do outro e podem ser localizados verticalmente dentro do VStack , horizontalmente no HStack e um acima do outro ZStack . ScrollView e ListView também estão disponíveis para nós. Você pode alternar e compartilhar esses contêineres para obter qualquer malha de elementos.


Ao combinar contêineres, você pode obter uma árvore bastante grande com um grande número de anexos. No entanto, o SwiftUI é otimizado especificamente para isso, portanto, o aninhamento profundo de contêineres não afeta o desempenho. Isso é afirmado no vídeo com wwdc (a partir das 15:32 ).


 var body: some View { NavigationView { VStack { NavigationLink(destination: LargeView(timeString: subtitle)) { Text("See Fullscreen") } Text("World Time").font(.system(size: 30)) } } } 

Como mostrar a barra de navegação?


Declarar um NavigationView não NavigationView suficiente; você deve especificar um título e estilo de navegação para a barra de navegação .


 NavigationView { VStack{}.navigationBarTitle(Text("World Time"), displayMode: .inline) } 

Observe que a função navigationBarTitle não é chamada no NavigationView , mas em sua View interna. DisplayMode é um parâmetro que indica o estilo da barra de navegação : grande ou padrão.


Existe um análogo do método viewDidLoad?


Se você deseja executar o código ao inicializar o View , pode fazê-lo adicionando a função onAppear {}. O OnAppear pode ser adicionado a qualquer View , por exemplo, ao VStack . Neste exemplo, quando o contêiner aparece na tela, uma solicitação http é feita para o servidor.


 struct ContentView : View { @State var statusString : String = "World Time" var body: some View { NavigationView { VStack { NavigationLink(destination:DetailView()) { Text("Go Detail") } Text(statusString).font(.system(size: 30)) }.onAppear { self.loadTime() } } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date())" } } } } 

Chamamos a função loadTime , que solicita a hora atual do servidor e retorna o modelo WorldTime . Não entraremos em ciclos na classe NetworkService ; você pode ver todo o código, tendo baixado os códigos-fonte. Link no final do artigo.

A variável var statusString foi renderizada para atribuir o horário atual posteriormente. A variável tem um atributo especial @State . O que ele quer dizer


Wrappers de propriedades ou o que é @State ?


O Swift 5.1 introduziu os chamados wrappers de propriedade (ou delegados de propriedade ). No SwiftUI, os wrappers de propriedades são usados ​​para atualizar ou vincular um dos parâmetros de exibição com nossa própria variável, por exemplo, o valor da opção Toggle .


O atributo @State é um atributo especial que é colocado antes de uma declaração de variável. Isso nos permite rastrear automaticamente alterações de propriedade sem código adicional. No exemplo acima, o texto "Hora mundial" mudará para a data atual assim que atualizarmos o valor statusString.


Para vincular valores ( Vinculação de propriedades ), podemos especificar um caractere especial $ antes do nome da variável no próprio código:


 struct DetailsView: View { @State var changeToggle: Bool var body: some View { Toggle(isOn: $changeToggle) { Text("Change Toggle") } } } 

Alterando a posição do comutador, o valor da variável também será alterado.


Os invólucros de propriedades são um componente muito importante do SwiftUI, acabei de mencioná-los de passagem. Para um conhecimento mais detalhado dos wrappers de propriedades, assista ao vídeo do wwdc aqui (a partir do 37º minuto), aqui (a partir do 12º minuto) e aqui (a partir do 19º minuto).


Como adicionar view ao tempo de execução?


É importante notar imediatamente que você não pode adicionar visualização a qualquer momento no sentido literal da palavra. SwiftUI é uma estrutura declarativa que renderiza a exibição inteira. No entanto, você pode definir várias condições dentro do corpo e atualizar o estado da exibição quando elas mudarem. Neste exemplo, usamos o grupo mais simples de @State – if com a variável isTimeLoaded .


 struct ContentView : View { @State var statusString : String = "World Time" @State var isTimeLoaded : Bool = false var body: some View { NavigationView { VStack { if isTimeLoaded { addNavigationLink() } Text(statusString).font(.system(size: 30)).lineLimit(nil) }.navigationBarTitle(Text("World Time"), displayMode: .inline) }.onAppear { self.loadTime() } } func addNavigationLink() -> some View { NavigationLink(destination: Text("124!!!")) { Text("Go Detail") } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date().description(with: Locale.current))" self.isTimeLoaded = true } } } } struct DetailView : View { var timeString : String var body : some View { Text(timeString).font(.system(size: 40)).lineLimit(nil) } } 

A propósito, você notou que a função addNavigationLink() não tem a palavra return ? Essa é outra inovação do Swift 5.1 - para funções com uma única expressão, agora é opcional escrever return . Mas você pode escrever.


Conclusão


Isso é apenas parte das perguntas e respostas do SwiftUI. Examinei questões gerais, espero que este artigo ajude os iniciantes a entender os principais pontos dessa estrutura. O SwiftUI ainda é bruto, mas sem dúvida será aprimorado.


A questão lógica é: vale a pena aprender o UIKit? Claro que sim . O UIKit é a base da programação no iOS e se desenvolverá ainda mais. Além disso, muitos componentes SwiftUI são um invólucro sobre o UIKit. Bem, até o momento não existem bibliotecas, estruturas, pods para o SwiftUI. Tudo terá que ser escrito por você. Portanto, é melhor estudar as duas abordagens do desenvolvimento - para que você seja um desenvolvedor mais valioso.


Você pode baixar as fontes do projeto aqui .


Obrigado por ler o artigo até o fim. Espero que você ache útil.


O que ler?


Source: https://habr.com/ru/post/pt455970/


All Articles