Olá! Eu sou a Giovanna Moeller, instrutora na Alura, e quero te desejar boas-vindas a mais um curso de iOS, agora com foco na arquitetura MVC.
Audiodescrição: Giovanna se descreve como uma mulher branca de cabelos loiros lisos e longos, sobrancelhas loiras, e olhos castanho-escuros. Ela veste uma camisa azul com o nome Alura em branco, e está sentada nos estúdios da Alura, com uma parede clara ao fundo iluminada em gradiente roxo e azul-escuro, e uma estante preta à esquerda da instrutora, com enfeites e pontos de iluminação amarela.
Durante este curso, vamos trabalhar em um projeto chamado Tasky, um aplicativo de lista de tarefas onde podemos, por exemplo, adicionar uma nova tarefa. Para exemplificar, vamos criar uma nova tarefa com o título "Estudar Swift" e a descrição "MVC", finalizando no botão "Salvar" abaixo.
Uma vez criadas as tarefas, podemos marcá-las como completas ou desmarcá-las. Além disso, conseguimos remover tarefas. Ao longo do processo, todas essas tarefas são armazenadas para evitar perder qualquer dado quando fecharmos o aplicativo e o abrirmos novamente.
Ao longo do curso, trabalharemos com a arquitetura MVC:
- Model (Modelo);
- View (Visão);
- E Controller (Controlador).
É muito importante aprender sobre essa arquitetura, pois é a arquitetura padrão utilizada pela Apple em seus aplicativos iOS quando estudamos sobre o framework UIkit.
Durante este projeto, também utilizaremos View Code, da abordagem programática. Sendo assim, não iremos utilizar o Storyboard, afinal, é isso que o mercado de trabalho pede.
Para realizar este curso, é importante ter conhecimentos prévios sobre a linguagem Swift e a construção de interfaces utilizando View Code. Não se preocupe, pois temos todos esses conteúdos aqui na plataforma da Alura!
Te aguardamos para a primeira aula!
Vamos iniciar nosso projeto?
Primeiramente, vamos acessar o Figma do projeto Tasky, cujo link ficará disponível para consulta.
Começaremos pela tela inicial, composta por duas imagens:
- O logotipo da Tasky na parte superior;
- E a imagem principal logo abaixo.
Além disso, há um texto e um botão "Vamos começar!" logo abaixo.
Main.storyboard
Agora vamos acessar o Xcode, onde já criamos o projeto, mas nada além disso. Para criá-lo, usamos o Storyboard, pois trabalhamos com o framework UIkit.
No entanto, como vamos utilizar o View Code, que é uma abordagem programática, não utilizaremos o Storyboard. Portanto, precisamos remover o arquivo Main
do Storyboard. Após clicar no arquivo Main
com o botão direito, basta selecionar a opção "Delete" e, em seguida, "Move to Trash".
Main
Após remover o arquivo, precisamos remover todas as suas referências. Para começar, vamos acessar a pasta do aplicativo e abrir a aba "Info". Nela, encontraremos uma tabela com a chave "Main storyboard file base name" definida como Main
. Vamos apagar essa linha teclando "Delete".
Na sequência, vamos abrir o arquivo Info
, localizado na pasta do projeto TaskyApp
. Neste arquivo, temos o "Application Scene Manifest". Clicaremos na seta à esquerda para expandir.
Continuaremos expandindo os itens "Scene Configuration", "Window Application Session Role", e "Item 0 (Default Configuration)", até encontrarmos o "Storyboard Name", que está definido como Main
. Apagaremos essa linha da mesma forma que fizemos anteriormente.
Com isso, removemos todas as referências do Storyboard.
ViewController
Agora precisamos configurar o arquivo SceneDelegate.swift
, para que a classe ViewController
seja a ViewController
inicial. Dessa forma, quando a pessoa usuária abrir o aplicativo, essa será a primeira ViewController
visualizada.
Começaremos renomeando a classe no arquivo ViewController.swift
de ViewController
para HomeViewController
para ficar mais significativo.
Primeiro vamos clicar sobre o arquivo ViewController.swift
no menu lateral esquerdo e renomeá-lo para HomeViewController.swift
. Feito isso, podemos renomear a classe na linha 10 do código.
HomeViewController.swift
:
// código omitido
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
SceneDelegate.swift
Feito isso, podemos voltar ao arquivo SceneDelegate.swift
e modificar o método scene()
da linha 15, realizando todos os ajustes necessários para utilizar o View Code.
Na linha 19, em vez de definir a variável _
, definiremos uma variável chamada windowScene
. Logo após essa linha, passaremos self.window
e instanciaremos um objeto do tipo UIWindow()
, passando o windowScene
como parâmetro.
SceneDelegate.swift
:
// código omitido
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
}
// código omitido
Também precisamos dizer que self.window?.rootViewController
, que é a ViewController
inicial, será igual a UINavigationController()
, onde selecionaremos a segunda opção com o rootViewController
, pois iremos utilizar navegação neste aplicativo.
Nesse caso, a rootViewController
será definida como HomeViewController()
. Após instanciar a ViewController
, por último, precisamos fazer um self.window?.makeKeyAndVisible()
.
// código omitido
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
self.window?.rootViewController = UINavigationController(rootViewController: HomeViewController())
self.window?.makeKeyAndVisible()
}
// código omitido
Assim, configuramos o aplicativo para utilizar o View Code. A última coisa que falta para terminar essa configuração inicial é importar as imagens e as cores.
De volta ao Figma, temos três imagens ao longo de todas as telas: o logotipo da Tasky; a imagem principal da tela inicial; e outra imagem secundária que aparece na tela de tarefas. Além disso, existem algumas cores que precisamos coletar os respectivos códigos para utilizá-las.
Com o Xcode aberto, vamos acessar o arquivo Assets
, onde importaremos as imagens. Após baixar todas no formato SVG (home-illustration.svg
, logo.svg
, e tasks-illustration.svg
), basta arrastá-las da pasta para o arquivo Assets
.
Como utilizamos o formato SVG, abriremos o menu lateral no ícone retangular do canto superior direito. No campo "Resizing" da seção "Image Set", marcaremos a caixa de seleção "Preserve Vector Data".
Feito isso, no campo "Scales", vamos alterar da opção "None" para "Single Scale" no menu suspenso. Como usamos SVG, ele não perde a qualidade, então é um ótimo formato de imagem para utilizar.
Também adicionaremos cores. Para isso, após clicar com o botão direito do mouse dentro do arquivo Assets
, vamos selecionar "New Color Set".
A primeira cor será chamada de DarkPurple
, que será um roxo mais escuro. Feito isso, vamos ajustar o campo "Appearances" na seção "Color Set" à direita. Em vez de "Any, Dark", mudaremos para "None". Dessa forma, teremos apenas uma cor.
No campo Input Method, na seção "Color" mais abaixo, vamos alterar de "8-bit (0-255)" para "8-bit Hexadecimal". Por fim, o código hexadecimal dessa primeira cor será #22183B
.
Agora que temos o tom de roxo escuro configurado, vamos criar outra cor. Com o botão direito, selecionamos "New Color Set" novamente, mas essa cor será um lilás, então chamaremos de Lilac
.
Da mesma forma, definiremos o campo "Appearances" como "None" em vez de "Any, Dark". O código hexadecimal agora é #5F5CFF
. Com isso, temos um tom de lilás mais escuro e azulado.
Constants.swift
Agora que importamos as imagens e as cores necessárias, para finalizar, criaremos um novo arquivo. Para começar, dentro da pasta do projeto TaskyApp
, criaremos uma nova pasta chamada "Utils". Nessa nova pasta, criaremos um arquivo do tipo "Swift File" chamado Constants.swift
.
Constants.swift
:
// código omitido
import Foundation
No escopo deste arquivo, criaremos um enum
para armazenar os valores das imagens e das cores que acabamos de importar no arquivo Assets
. Dessa forma, não utilizaremos como valores mágicos.
Na linha 10, definiremos um enum
chamado AssetsConstants
. Entre chaves, começaremos referenciando: criaremos uma variável estática (static let
) chamada homeIllustration
na linha 11.
Essa variável será igual ao nome exato da imagem que colocamos no arquivo Assets
, ou seja, home-illustration
entre aspas duplas.
// código omitido
import Foundation
enum AssetsConstants {
static let homeIllustration = "home-illustration"
}
Na linha abaixo, faremos o mesmo com static let tasksIllustration
, que será igual a tasks-illustration
entre aspas duplas. Para o logotipo, vamos declarar uma static let
chamada logo
.
Por fim, para as cores, vamos declarar uma static let
chamada darkPurple
na linha 14, que será igual a DarkPurple
; e uma static let
chamada lilac
, que será igual a Lilac
.
// código omitido
import Foundation
enum AssetsConstants {
static let homeIllustration = "home-illustration"
static let tasksIllustration = "tasks-illustration"
static let logo = "logo"
static let darkPurple = "DarkPurple"
static let lilac = "Lilac"
}
Esse processo é somente para centralizar tudo o que importamos e evitar escrever em string toda vez que formos utilizar essas imagens e cores na nossa aplicação. Afinal, às vezes podemos cometer algum tipo de erro, então consideramos isso uma boa prática.
HomeViewController.swift
Para recapitular e verificar se está em funcionamento, voltaremos ao arquivo HomeViewController.swift
. No escopo da função viewDidLoad()
, apagaremos o comentário da linha 14.
No lugar disso, escreveremos view.backgroundColor
para aplicar uma cor de fundo, que será igual a UIColor()
recebendo o parâmetro named
. Nesse caso, em vez de passarmos DarkPurple
em formato de string, vamos chamar AssetsConstants.darkPurple
.
HomeViewController.swift
:
// código omitido
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(named: AssetsConstants.darkPurple)
}
}
Com o comando "Command + R", podemos executar o aplicativo. Normalmente, ele demora um pouco na primeira execução, mas ao final, teremos o aplicativo exibindo a tela inicial com a cor de fundo roxa.
Agora que já configuramos todo o projeto no Xcode, começaremos a construir a tela inicial. Nos encontramos no próximo vídeo!
Neste vídeo, começaremos a construir a tela inicial e adicionaremos os primeiros elementos necessários.
Observando o projeto Tasky no Figma, percebemos que a tela inicial é composta por um fundo gradiente, que vai do branco até o lilás que importamos no arquivo Assets
. Dito isso, vamos voltar ao Xcode.
ViewController
Poderíamos adicionar esse código diretamente na classe HomeViewController
, mas haveria um problema: se quiséssemos reutilizar esse código em outra tela, precisaríamos copiar e colar o código, o que é uma má prática, pois resultaria em duplicação de código.
Sendo assim, a melhor maneira para adicionar o fundo gradiente é criar uma extensão da ViewController
, onde criaremos um método para adicionar um fundo gradiente na tela.
O Swift no iOS com o UIkit é um pouco complicado para trabalhar com elementos como gradiente. O código não é tão simples quanto colocar uma cor de fundo, como fizemos na linha 14 do arquivo
HomeViewController.swift
, utilizandoview.backgroundColor = UIColor()
.
Começaremos criando uma nova pasta no projeto. Para isso, clicamos sobre TaskyApp
com o botão direito do mouse, selecionamos "New Group", e nomeamos como "Extensions".
Nessa pasta, vamos criar um novo arquivo do tipo "Swift File" chamado UIViewController+
. O sinal de +
ao final é uma boa prática quando trabalhamos com arquivos que são extensões.
UIViewController+.swift
:
// código omitido
import Foundation
UIViewController
Na linha 10, podemos começar a criar uma extension
chamada UIViewController
. No escopo dessa extension
, adicionaremos um método (func
) chamado addGradientBackground()
.
No momento, o Xcode indica que não consegue encontrar
UIViewController
, pois ainda precisamos importar o UIkit. Portanto, na linha 8, em vez deimport Foundation
, usaremosimport UIKit
.
// código omitido
import UIKit
extension UIViewController {
func addGradientBackground() {
}
}
addGradientBackground()
Agora vamos implementar o método addGradientBackground()
. Podemos começar definindo uma constante let
na linha 12 chamada gradientBackground
, que será igual a CAGradientLayer()
, basicamente uma camada de gradiente que criamos com essa classe.
Feito isso, vamos definir na linha 13 que gradientBackground.frame
será igual a view.bounds
, pois queremos que o tamanho do gradiente seja igual ao tamanho total da tela, afinal, ele será o fundo.
// código omitido
extension UIViewController {
func addGradientBackground() {
let gradientBackground = CAGradientLayer()
gradientBackground.frame = view.bounds
}
}
Em seguida, na linha 14 logo abaixo, vamos definir gradientBackground.colors
, onde passaremos as cores que iremos utilizar no gradiente. Isso será um array, então abrimos e fechamos colchetes.
O primeiro elemento do array será a cor do topo, então passaremos UIColor.white
. Além disso, precisamos adicionar .cgColor
ao final, pois a variável colors
exige o parâmetro cgColor
.
Feito isso, colocaremos uma vírgula e seguiremos com a segunda opção de UIColor()
, com o parâmetro named
definido como AssetsConstants.lilac
, para aplicar a cor lilás adicionada em Assets
.
Como essa cor retorna uma opcional, precisamos utilizar o ponto de interrogação (?
) após UIColor()
, além do parâmetro cgColor
ao final.
Caso haja algum erro com a cor importada, adicionamos dois sinais de interrogação (
??
) apóscgColor
para utilizar o tom de roxo normal, ou seja,UIColor.purple.cgColor
.
Por fim, precisamos adicionar o método view.layer.insertSublayer()
na linha 15. No parâmetro layer
, passaremos gradientBackground
, variável criada anteriormente; e no parâmetro at
, colocaremos o índice zero (0
).
O gradiente será a view de fundo, por isso selecionamos o primeiro índice.
// código omitido
extension UIViewController {
func addGradientBackground() {
let gradientBackground = CAGradientLayer()
gradientBackground.frame = view.bounds
gradientBackground.colors = [UIColor.white.cgColor, UIColor(named: AssetsConstants.lilac)?.cgColor ?? UIColor.purple.cgColor]
view.layer.insertSublayer(gradientBackground, at: 0)
}
}
Esse é o código para construir um fundo gradiente. Os processos costumam ser um pouco mais complicados no UIkit, conforme dito anteriormente, mas isso é completamente normal.
addGradientBackground()
Vamos voltar para o arquivo HomeViewController.swift
. Na função viewDidLoad()
da linha 14, vamos apagar todo o trecho de view.backgroundColor
e chamar o método addGradientBackground()
.
HomeViewController.swift
:
// código omitido
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addGradientBackground()
}
}
Feito isso, podemo executar o aplicativo com o atalho "Command + R". Dessa forma, já teremos o fundo gradiente aplicado na tela inicial.
De volta ao projeto no Figma, ainda precisamos inserir as duas primeiras imagens: o logotipo da Tasky e a ilustração principal, ambas na tela inicial.
No Xcode, já temos essas duas imagens no arquivo Assets
, então temos tudo preparado. Começaremos criando essas imagens no arquivo HomeViewController.swift
.
Logo acima da função viewDidLoad()
, na linha 12, escreveremos o código para inserir elementos na tela através da abordagem programática. Portanto, iniciamos declarando uma variável private lazy var
chamada logoImageView
, que será do tipo UIImageView
.
// código omitido
import UIKit
class HomeViewController: UIViewController {
private lazy var logoImageView: UIImageView = {
}()
override func viewDidLoad() {
super.viewDidLoad()
addGradientBackground()
}
}
No escopo da variável, vamos declarar um let
chamado imageView
, que será igual a UIImageView()
, recebendo o parâmetro image
definido como UIImage()
. Entre os parênteses de UIImage()
, o parâmetro named
será definido como AssetsConstants.logo
.
Note como o
AssetsConstants
criado anteriormente nos ajuda no processo. Com ele, não precisamos escrever as strings, pois já temos uma referência delas. Essa é uma boa prática muito importante.
// código omitido
private lazy var logoImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: AssetsConstants.logo))
}()
// código omitido
Em seguida, na linha 14, precisamos fazer um imageView.translatesAutoresizingMaskIntoConstraints
, que será igual a false
, uma vez que utilizamos do View Code.
Logo abaixo, na linha 15, chamaremos imageView.contentMode
e definiremos como .scaleAspectFit
para a imagem ficar bem encaixada.
Para finalizar, passamos return imageView
na linha 16.
// código omitido
private lazy var logoImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: AssetsConstants.logo))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
// código omitido
Podemos copiar o trecho de código entre as linhas 12 e 17, pois será basicamente o mesmo para a segunda imagem. Após colar o trecho na linha 19, mudaremos o nome de logoImageView
para mainIllustrationImageView
.
Além disso, na linha 20, em vez de passar logo
, passaremos homeIllustration
. As outras informações serão iguais para ambas as imagens, então vamos manter os trechos das linhas 21 e 22.
// código omitido
private lazy var mainIllustrationImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: AssetsConstants.homeIllustration))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
// código omitido
addSubviews()
Agora precisamos adicionar as duas imagens na tela, além de adicionar as constraints para posicioná-las conforme esperado.
Para isso, criaremos dois métodos após a função viewDidLoad()
no arquivo HomeViewController.swift
. Na linha 31, adicionaremos um private func
chamado addSubviews()
.
No escopo desse método, na linha 32, vamos chamar view.addSubview()
recebendo logoImageView
entre parênteses. Logo depois, na linha 33, adicionaremos view.addSubview(mainIllustrationImageView)
.
// código omitido
private func addSubviews() {
view.addSubview(logoImageView)
view.addSubview(mainIllustrationImageView)
}
// código omitido
setupConstraints()
Agora, a partir da linha 36, vamos declarar um private func
chamado setupConstraints()
. No escopo do método, na linha 37, chamaremos NSLayoutConstraint.activate()
.
Entre os parênteses de activate()
, vamos adicionar as constraints. Começaremos chamando logoImageView
e colocando uma topAnchor
.
Ela será equalTo: view.safeAreaLayoutGuide
para pegar a área segura da aplicação que tira a data e a hora, e finalizaremos com um topAnchor
, que é a constraint do topo.
Por último, vamos centralizar essa imagem. Para isso, adicionaremos logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
.
// código omitido
private func setupConstraints() {
NSLayoutConstraint.activate([
logoImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
}
// código omitido
Para a segunda imagem, vamos chamar mainIllustrationImageView.topAnchor.constraint()
e selecionar a opção com equalTo
e constant
. Em equalTo
, colocaremos logoImageView.bottomAnchor
, pois ela ficará abaixo do logo. Como constant
, vamos colocar um valor de 8.0
.
Da mesma forma, iremos centralizá-la com mainIllustrationImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
.
// código omitido
private func setupConstraints() {
NSLayoutConstraint.activate([
logoImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
mainIllustrationImageView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 8.0),
mainIllustrationImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
}
// código omitido
Por fim, podemos chamar os dois métodos que acabamos de criar na função viewDidLoad()
, da linha 26. Na linha 29, chamaremos addSubviews()
, e na linha 30, setupConstraints()
.
// código omitido
override func viewDidLoad() {
super.viewDidLoad()
addGradientBackground()
addSubviews()
setupConstraints()
}
// código omitido
Ao executar o código com "Command + R", verificamos que, como resultado, recebemos o logotipo e a imagem principal na tela inicial.
Agora que adicionamos as duas imagens na tela e também o fundo gradiente, falta adicionar o texto e o botão. Faremos isso no próximo vídeo!
O curso iOS: construindo uma lista de tarefas com MVC com View Code possui 139 minutos de vídeos, em um total de 51 atividades. Gostou? Conheça nossos outros cursos de iOS em Mobile, ou leia nossos artigos de Mobile.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.