Desenvolvendo o ReceitADA: Um Guia Passo a Passo para Criar as Primeiras Páginas do App com SwiftUI

Lucas Dantas
Apple Developer Academy | UFPE
14 min readApr 11, 2024

Ao longo deste artigo, vou guiá-los passo a passo na criação das páginas 1 e 2 que compõem o ReceitADA. Vamos começar!

Começando…

Para começar a desenvolver o aplicativo ReceitADA, precisamos criar um projeto iOS no Xcode. Abra também o figma do receitada, lá vamos ter acesso ao design completo com as cores corretas.

Criando a primeira tela

Antes de tudo, como podemos ver no design, vamos precisar de uma imagem de comida, baixar uma imagem e adicionar ao arquivo Assets.xcassets no Xcode, para isso, abra o Assets.xcassets, clique no botão "+"-> import… -> selecione o arquivo da imagem que vai utilizar.

Para desenvolver a tela inicial do nosso aplicativo, precisamos criar um novo arquivo SwiftUI View na pasta principal. Este arquivo será chamado LandingView.swift.

Para criar o retângulo amarelo com o texto “Receitada” no topo da tela, vamos definir uma ZStack. A ZStack é uma estrutura que nos permite empilhar elementos os sobrepondo na interface.

Dentro da ZStack, criaremos um retângulo com a cor amarela e posicionaremos o texto “Receitada” sobre ele.

Substitua o código dentro de var body: some View { por:

ZStack{
Rectangle()
.foregroundColor(.yellow)
.ignoresSafeArea()
.frame(height: 200)
.cornerRadius(20.0)
Text("Receitada")
.font(.largeTitle)
.fontWeight(.bold)
}
  • ZStack: define a estrutura onde cada novo elemento é posto em cima do anterior
  • Rectangle(): literalmente cria um retângulo que pode ser manipulado
  • .foregroundColor: modificador para definir a cor do conteúdo visual de uma view, usamos para deixar o retângulo com cor amarela definindo como .yellow
  • .ignoreSafeArea(): esse modificador é crucial para podermos fazer o retângulo extrapolar o limite superior da tela atingindo o design que queremos
  • .frame(height: 200): afeta a geometria da view, permitindo especificar largura, altura, alinhamento e posicionamento, nesse caso especificamos a altura como 200
  • .cornerRadius(20.0): define o corner radius (em web chamamos de border radius), serve para arredondar as bordas do retângulo
  • Text("Receitada"): cria um elemento que mostra um texto especificado
  • .font e .fontWeight: usados para especificar o estilo da fonte do texto

Seu preview deve estar da seguinte forma:

Agora vamos adicionar o texto que vem logo a seguir, abaixo do retângulo:

ZStack{
Rectangle()
.foregroundColor(.yellow)
.frame(height: 200)
.ignoresSafeArea()
.cornerRadius(20.0)
Text("Receitada")
.font(.largeTitle)
.fontWeight(.bold)
}

// Texto abaixo do retângulo
Text("Você com a colher, nós com as dicas!")
.font(.headline)
.fontWeight(.semibold)
.padding()
  • .padding(): modificador que adiciona espaço ao redor do elemento a ser modificado, é útil para ajustar o espaçamento entre elementos em um layout e para melhorar a legibilidade e a aparência visual do conteúdo

A seguir, temos quatro conjuntos de ícones acompanhados de texto. Para implementar isso, faremos uso das estruturas VStack e HStack, juntamente com a View Image.

Para o primeiro par ícone-texto, vamos criar uma HStack, estrutura do swiftUI que nos permite colocar elementos lado a lado horizontalmente.

HStack {
Image(systemName: "carrot.fill")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Explore deliciosas receitas na palma da sua mão e torne-se le chef que sempre quis ser!")
.font(.footnote)
.frame(height: 40)
}
  • HStack: inicia uma nova horizontal stack (HStack), onde os elementos estão dispostos horizontalmente lado a lado
  • Image(systemName: "carrot.fill"): o image() cria uma imagem, usando o parâmetro systemName podemos renderizar ícones do swiftUI
  • .resizable(): modificador que torna a imagem redimensionável
  • .scaledToFit(): modificador que redimensiona a imagem para se ajustar ao espaço disponível, mantendo sua proporção, usamos junto com frame(width: 40, height: 40)para fazer a imagem se ajustar a esse quadrado 40x40

Agora, replique essa HStack para termos 4 no total, e as ponha dentro de uma VStack

  • VStack: assim como o HStack, que organiza os elementos horizontalmente, a VStack organiza os elementos verticalmente

Substitua os textos e systemName dos ícones, e na VStack ponha o parâmetro VStack(spacing: 10) {… para que tenhamos um bom espaçamento entre os pares de texto e ícone. Após a chave de fechamento da VStack, adicione os modificadores .padding(.horizontal, 30) e .padding(.bottom, 30) para ter um bom espaçamento das laterais da tela.

  • .horizontal: indica um padding apenas nas laterais do elemento. Podemos ainda usar .top(cima) .bottom(baixo) .leading(esquerda) .trailing(direita) e .vertical(cima e baixo)

Adicionamos também .frame(width: 280, height: 60) nos Texts para que eles fiquem contidos num espaço parecido com o especificado no design.

O resultado esperado e código devem estar assim:

import SwiftUI

struct LandingView: View {
var body: some View {
ZStack{
Rectangle()
.foregroundColor(.yellow)
.frame(height: 200)
.ignoresSafeArea()
.cornerRadius(20.0)
Text("Receitada")
.font(.largeTitle)
.fontWeight(.bold)
}
Text("Você com a colher, nós com as dicas!")
.font(.headline)
.fontWeight(.semibold)
.padding()
VStack(spacing: 10) {
HStack {
Image(systemName: "carrot.fill")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Explore deliciosas receitas na palma da sua mão e torne-se le chef que sempre quis ser!")
.font(.footnote)
.frame(width: 280, height: 60)
}
HStack {
Image(systemName: "vial.viewfinder")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Navegue por categorias, encontre receitas por ingredientes ou explore os favoritos dos chefs!")
.font(.footnote)
.frame(width: 280, height: 60)
}
HStack {
Image(systemName: "list.bullet.clipboard")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Siga instruções passo a passo, com imagens e dicas úteis para criar pratos perfeitos.")
.font(.footnote)
.frame(width: 280, height: 60)
}
HStack {
Image(systemName: "heart.fill")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Guarde suas receitas favoritas e compartilhe suas criações culinárias com amigos e familiares.")
.font(.footnote)
.frame(width: 280, height: 60)
}
}
.padding(.horizontal, 30)
.padding(.bottom, 30)
}
}

#Preview {
LandingView()
}

Agora, precisamos adicionar uma imagem logo abaixo dos ícones e textos, dentro da VStack, abaixo das HStacks contendo textos, colocamos:

Image("FileComFritas")
.resizable()
.scaledToFill()
.frame(maxWidth: .infinity, maxHeight: 120.0)
.cornerRadius(20)
.padding(.vertical)
  • "FileComFritas": é o nome da minha imagem no arquivo assets, no lugar ponha no nome da sua imagem
  • .resizable(): permite que a imagem seja redimensionada para caber em seu container
  • .scaledToFill(): ajusta o tamanho da imagem para preencher completamente seu container, preservando a proporção da imagem

Para finalizar a primeira tela, precisamos colocar um NavigationLink em forma de botão, que vai nos levar para a próxima tela, para isso, vamos começar colocando todo o nosso conteúdo em uma NavigationStack, esta, define uma pilha de telas, onde é possível navegar por elas.

Ponha todo o conteúdo dentro de var body: some View {… em uma NavigationStack{...}

Antes de criar nosso botão, precisamos de um lugar pra ele apontar, este será uma nova view que criaremos, a MainView. Dentro da pasta principal do projeto, crie um arquivo so tipo swiftUI View chamado MainView.swift:

Agora vamos criar o botão, logo após a VStack que contém os textos e imagem, vamos criar nosso NavigationLink:

NavigationLink(destination: MainView()) {
Text("Começar a cozinhar")
.foregroundColor(.black)
.font(.subheadline)
.fontWeight(.bold)
.frame(height: 50)
.frame(maxWidth: .infinity, maxHeight: 60.0)
}
.background(Color(.yellow))
.cornerRadius(10)
.padding()
.padding(.bottom, 50)
  • …(maxWidth: .infinity, … : indica que o frame ocupa o máximo de espaço que puder
  • background(Color(.yellow)): background te permite modificar o background de um elemento, neste caso, adicionando a cor amarela

Agora você precisa fazer o arquivo principal ReceitADAApp.swift apontar para nosso LandingView(), para isso, faça a seguinte alteração em ReceitADAApp.swift:

import SwiftUI

@main
struct ReceitADAApp: App {
var body: some Scene {
WindowGroup {
// aqui antes era ContentView(), troque para LandingView()
LandingView()
}
}
}

Seu código final do arquivo LandingView.swift deve estar como a seguir, já pode buildar no botão play no canto superior esquerdo e usar no simulador:

import SwiftUI

struct LandingView: View {
var body: some View {
NavigationStack {
ZStack{
Rectangle()
.foregroundColor(.yellow)
.ignoresSafeArea()
.frame(height: 200)
.cornerRadius(20.0)
Text("Receitada")
.font(.largeTitle)
.fontWeight(.bold)
}
Text("Você com a colher, nós com as dicas!")
.font(.headline)
.fontWeight(.semibold)
.padding()
VStack(spacing: 10) {
HStack {
Image(systemName: "carrot.fill")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Explore deliciosas receitas na palma da sua mão e torne-se le chef que sempre quis ser!")
.font(.footnote)
.frame(width: 280, height: 60)
}
HStack {
Image(systemName: "vial.viewfinder")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Navegue por categorias, encontre receitas por ingredientes ou explore os favoritos dos chefs!")
.font(.footnote)
.frame(width: 280, height: 60)
}
HStack {
Image(systemName: "list.bullet.clipboard")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Siga instruções passo a passo, com imagens e dicas úteis para criar pratos perfeitos.")
.font(.footnote)
.frame(width: 280, height: 60)
}
HStack {
Image(systemName: "heart.fill")
.resizable()
.foregroundColor(.yellow)
.scaledToFit()
.frame(width: 40, height: 40)
Text("Guarde suas receitas favoritas e compartilhe suas criações culinárias com amigos e familiares.")
.font(.footnote)
.frame(width: 280, height: 60)
}
Image("FileComFritas")
.resizable()
.scaledToFill()
.frame(maxWidth: .infinity, maxHeight: 120.0)
.cornerRadius(20)
.padding(.vertical)
}
.padding(.horizontal, 30)
.padding(.bottom, 30)
NavigationLink(destination: MainView()) {
Text("Começar a cozinhar")
.foregroundColor(.black)
.font(.subheadline)
.fontWeight(.bold)
.frame(height: 50)
.frame(maxWidth: .infinity, maxHeight: 60.0)
}
.background(Color(.yellow))
.cornerRadius(10)
.padding()
.padding(.bottom, 50)
}
}
}

#Preview {
LandingView()
}

Criando a segunda tela

Para a segunda tela, precisamos das informações das receitas, vamos criar um arquivo Swift normal, não SwiftUI View, chamado Recipe.swift, vamos pôr nele o código da classeRecipe

import Foundation

class Recipe: Identifiable {
let id: UUID
var name: String
var level: Double
var timeInMinutes: Int
var imageName: String
var ingredients: [String]
var steps: [(name: String, text: String)]

init(name: String, level: Double, timeInMinutes: Int, imageName: String, ingredients: [String], steps: [(name: String, text: String)]) {
self.id = UUID()
self.name = name
self.level = level
self.timeInMinutes = timeInMinutes
self.imageName = imageName
self.ingredients = ingredients
self.steps = steps
}
}
  • Identifiable: protocolo ao qual Recipe conforma, significa que cada objeto do tipo Recipe deve ter um identificador, nesse caso usamos UUID
  • UUID: é um tipo que significa Universally Unique Identifier (Identificador universalmente único)
  • var steps: [(name: String, text: String)] : aqui eu declaro uma variável chamada steps, essa variável será um Array de Tuplas, e essas Tuplas por sua vez serão no formato (nome do passo, texto do passo), duas strings

Abaixo da definição da classe, você pode instanciar receitas dentro de uma lista que usaremos mais tarde, ou pode dar a classe para o chatGPT e pedir para ele fazer isso, de qualquer forma, aqui está o exemplo de como fiz a partir do chatGPT, cole o código abaixo da declaração da classe:

let recipesStored = [
Recipe(
name: "File com Fritas",
level: 4.5,
timeInMinutes: 130,
imageName: "FileComFritas",
ingredients: ["File de frango", "Batatas", "Sal", "Pimenta", "Óleo"],
steps: [
("Prepare the chicken", "Cut the chicken into pieces and marinate with salt and pepper."),
("Cook the fries", "Peel and cut the potatoes into strips. Fry them until golden brown."),
("Cook the chicken", "Grill or fry the chicken until cooked thoroughly."),
("Serve", "Arrange the chicken and fries on a plate. Sprinkle with additional salt and pepper if desired.")
]
),
Recipe(
name: "Hambúrguer",
level: 2.5,
timeInMinutes: 20,
imageName: "Hamburguer",
ingredients: ["Carne moída", "Pão de hambúrguer", "Queijo", "Alface", "Tomate"],
steps: [
("Prepare the burger patties", "Season the ground beef and form into patties."),
("Cook the patties", "Grill or fry the patties until cooked to your preference."),
("Assemble the burger", "Place the cooked patties on buns and add cheese, lettuce, and tomato."),
("Serve", "Enjoy your homemade burger!")
]
),
Recipe(
name: "Kebab",
level: 4.5,
timeInMinutes: 40,
imageName: "Kebab",
ingredients: ["Carne de cordeiro", "Pão sírio", "Tomate", "Cebola", "Molho de iogurte"],
steps: [
("Marinate the lamb", "Marinate the lamb meat with spices and yogurt."),
("Prepare the skewers", "Thread the marinated meat onto skewers with vegetables."),
("Grill the kebabs", "Grill the kebabs until the meat is cooked and tender."),
("Serve", "Serve the kebabs hot with a side of yogurt sauce.")
]
),
Recipe(
name: "Pasteis",
level: 4.5,
timeInMinutes: 35,
imageName: "Pasteis",
ingredients: ["Massa de pastel", "Carne moída", "Queijo", "Óleo para fritar"],
steps: [
("Prepare the filling", "Cook the ground beef with onions and spices until browned."),
("Fill the pastries", "Place a spoonful of the beef mixture and cheese onto each pastry sheet."),
("Seal the pastries", "Fold the pastry sheets over the filling and crimp the edges with a fork."),
("Fry the pastries", "Heat oil in a pan and fry the pastries until golden and crispy."),
("Serve", "Serve the pastries hot with dipping sauce.")
]
),
Recipe(
name: "Pizza",
level: 4.7,
timeInMinutes: 25,
imageName: "Pizza",
ingredients: ["Massa de pizza", "Molho de tomate", "Queijo", "Presunto", "Azeitonas"],
steps: [
("Prepare the dough", "Roll out the pizza dough and place it on a pizza pan."),
("Spread the sauce", "Spread a layer of tomato sauce evenly over the dough."),
("Add toppings", "Sprinkle grated cheese, sliced ham, and olives over the sauce."),
("Bake the pizza", "Preheat the oven and bake the pizza until the crust is golden and the cheese is bubbly."),
("Serve", "Slice the pizza and serve hot.")
]
),
Recipe(
name: "Quesadillas",
level: 4.4,
timeInMinutes: 30,
imageName: "Quesadillas",
ingredients: ["Tortillas", "Queijo cheddar", "Frango desfiado", "Tomate", "Creme de leite"],
steps: [
("Prepare the filling", "Cook and shred the chicken. Slice the tomatoes and grate the cheese."),
("Assemble the quesadillas", "Place a tortilla on a skillet. Add cheese, chicken, tomatoes, and sour cream on one half of the tortilla."),
("Fold the tortilla", "Fold the other half of the tortilla over the filling to form a half-moon shape."),
("Cook the quesadillas", "Cook the quesadillas on both sides until the cheese is melted and the tortilla is crispy."),
("Serve", "Slice the quesadillas into wedges and serve hot with salsa and guacamole on the side.")
]
),
Recipe(
name: "Salada Caesar",
level: 4.3,
timeInMinutes: 115,
imageName: "SaladaCeasar",
ingredients: ["Alface romana", "Croutons", "Queijo parmesão", "Molho Caesar", "Peito de frango grelhado"],
steps: [
("Prepare the salad", "Wash and chop the romaine lettuce into bite-sized pieces."),
("Grill the chicken", "Season the chicken breasts and grill until cooked through. Slice them into strips."),
("Assemble the salad", "In a large bowl, toss the lettuce with croutons, Parmesan cheese, and Caesar dressing."),
("Add the chicken", "Top the salad with grilled chicken strips."),
("Serve", "Serve the Caesar salad with extra dressing on the side.")
]
),
Recipe(
name: "Torta Alemã",
level: 4.9,
timeInMinutes: 50,
imageName: "TortaAlema",
ingredients: ["Biscoitos de chocolate", "Creme de leite", "Chocolate meio amargo", "Açúcar", "Manteiga"],
steps: [
("Prepare the crust", "Crush the chocolate cookies into fine crumbs. Mix with melted butter and press into a pie dish."),
("Make the filling", "Melt chocolate and sugar together. Whip cream until stiff peaks form."),
("Combine", "Fold the chocolate mixture into the whipped cream and pour into the crust."),
("Chill", "Refrigerate the torte for several hours until set."),
("Serve", "Slice and serve the German torte chilled.")
]
)
]
  • Você pode utilizar quantas dessas Recipes quiser, mas para cada imageName, precisamos baixar uma imagem colocá-la no arquivo Assets.xcassets assim como fizemos com a primeira imagem, utilizando o nome em imageName igual ao nome do arquivo em Assets.xcassets

No arquivo MainView.swift, vamos criar uma View chamada RecipeRowView da seguinte forma:


import SwiftUI

struct MainView: View {
var body: some View {
Text("Hello, World!")
}
}

// nova View criada
struct RecipeRowView: View {
var body: some View {
// aqui vamos colocar pôr código
}
}

#Preview {
MainView()
}

Nela, vamos construir como ficaria apenas 1 item da lista de recipes. Para visualizar o que estamos fazendo, vamos colocar o RecipeRowView() dentro do #Preview

#Preview {
RecipeRowView()
}

Nosso item da lista (RecipeRowView) precisa de uma Recipe para saber o que mostrar, vamos declarar em RecipeRowView, uma variável do tipo Recipe, que recebe, inicialmente, a primeira receita da nossa lista storedRecipes:

struct RecipeRowView: View {
// aqui declaramos a nova variável
var recipe: Recipe = storedRecipes[0]

var body: some View {
HStack {
Image(recipe.imageName)
.resizable()
.scaledToFill()
.frame(width: 50, height: 50)
.cornerRadius(5)
}
}
}

Dentro do nosso body, definimos uma HStack, pois num item da lista, nossos elementos (imagem, estrelas + texto, e o tempo estão lado a lado). Ainda dentro da HStack, criamos a imagem e seu container, chamando pelo nome da imagem que definimos antes.

A tela deve estar assim:

Em seguida adicionamos uma VStack que vai conter o Nome da Receita e as estrelas, usamos uma VStack para colocar os dois porque estão dispostos um sobre o outro.

starStack é uma função que recebe um parâmetro do tipo Double e retorna uma View, ela é responsável por gerar as estrelas da forma correta com base na nota de dificuldade, cole essa função no seu código após a declaração de RecipeRowView:

struct RecipeRowView: View {
var recipe: Recipe = storedRecipes[0]

var body: some View {
HStack {
Image(recipe.imageName)
.resizable()
.scaledToFill()
.frame(width: 50, height: 50)
.cornerRadius(5)
VStack(alignment: .leading) {
Text(recipe.name)
.font(.headline)
.fontWeight(.regular)
// aqui chamamos nossa função para mostrar as estrelas
starStack(of: recipe.level)
}
}
}
}

func starStack(of level: Double) -> some View {
let fullStarCount = Int(level)
let halfStar: Bool = level - Double(fullStarCount) >= 0.5
// true se tem um .5 .6 .7... na nota
let emptyStarCount = 5 - fullStarCount - (halfStar ? 1 : 0)

return HStack(spacing: 0) {
ForEach(0..<fullStarCount) { _ in
Image(systemName: "star.fill")
.foregroundColor(.yellow)
}

if halfStar {
Image(systemName: "star.fill.left")
.foregroundColor(.yellow)
}

ForEach(0..<emptyStarCount) { _ in
Image(systemName: "star")
.foregroundColor(.yellow)
}
}
}

#Preview {
RecipeRowView()
}

Abaixo da definição de starStack() vamos definir a função formatTime que recebe como entrada um número inteiro (tempo em minutos da receita) e retorna uma string no formato que queremos o tempo sendo mostrado.
Ex: formatTime(timeInMinutes: 70) -> "1h 10min"

func formatTime(timeInMinutes: Int) -> String {
let hours = timeInMinutes / 60
let minutes = timeInMinutes % 60

if hours > 0 {
return "\(hours)h \(minutes)min"
} else {
return "\(minutes)min"
}
}

Agora, em RecipeRowView vamos adicionar nosso ultimo elemento, o texto representando o tempo da receita, faremos isso com formatTime()

struct RecipeRowView: View {
var recipe: Recipe = storedRecipes[0]

var body: some View {
HStack {
Image(recipe.imageName)
.resizable()
.scaledToFill()
.frame(width: 50, height: 50)
.cornerRadius(5)
VStack(alignment: .leading) {
Text(recipe.name)
.font(.headline)
.fontWeight(.regular)
starStack(of: recipe.level)
}
// código adicionado
Spacer()
Text(formatTime(timeInMinutes: recipe.timeInMinutes))
.foregroundColor(.gray)
}
}
}

Adicionamos um Spacer() para afastar a imagem, nome e estrelas do tempo.

Sua tela deve estar assim:

Troque o preview para MainView(), vamos trabalhar nela:

#Preview {
MainView()
}

Primeiro vamos substituir oText("Hello, world") padrão por uma List dentro do body da MainView, e declarar uma variável recipes recipes que recebe o valor de storedRecipes:

struct MainView: View {
var recipes: [Recipe] = storedRecipes

var body: some View {
List(recipes) { recipe in
// Aqui dentro, podemos acessar cada recipe individualmente
// e definir como queremos que ela seja exibida na lista
}
}
}
  • List: List é um elemento de interface em SwiftUI que permite exibir uma lista de itens na tela. No caso, estamos criando uma lista para mostrar as receitas contidas no array
  • recipe in: indica que estamos percorrendo cada item (recipe) no array de recipes

Agora basta chamar, dentro da List a nossa RecipeRowView para cada recipe, logo, nossa MainView fica assim:

struct MainView: View {
var recipes: [Recipe] = storedRecipes

var body: some View {
List(recipes) { recipe in
RecipeRowView(recipe: recipe)
}
.listStyle(PlainListStyle())
.navigationTitle("Receitas")
.navigationBarTitleDisplayMode(.large)
}
}
  • .listStyle(PlaiListStyle()): modificador que altera o estilo padrão da nossa lista, para atingir o design desejado
  • .navigationTitle: modificador que vai adicionar um Título nessa tela quando estiver no fluxo de navegação
  • .navigationBarTitleDisplayMode(.large): usado para definir o modo de exibição do título da barra de navegação como “grande”

Para enxergarmos nosso título, já que ele é feito para navegação entre telas, vamos colocar uma NavigationStack no nosso #Preview:

#Preview {
NavigationStack {
MainView()
}
}

Agora para terminar nossa segunda tela, vamos adicionar um "+" no canto superior direito, como no design. Para isso, vamos usar o modificador .toolbar e nele pôr uma Image:

struct MainView: View {
var recipes: [Recipe] = storedRecipes

var body: some View {
List(recipes) { recipe in
RecipeRowView(recipe: recipe)
}
.listStyle(PlainListStyle())
.navigationTitle("Receitas")
.navigationBarTitleDisplayMode(.large)
// código adicionado
.toolbar {
Image(systemName: "plus.circle.fill")
.font(.system(size: 25))
.foregroundColor(.yellow)
}
}
}
  • .toolbar: é usado para adicionar barras de ferramentas à interface do usuário. Uma barra de ferramentas é uma coleção de botões, menus ou outros controles que fornecem funcionalidades adicionais para o usuário
  • .font(.system(size: 25)): em swiftUI podemos alterar o tamanho de um ícone usando font size

Segunda tela pronta, e você já pode rodar para testar!

Finalizando

Obrigado por ler! Para terminar o projeto, você pode tentar se aventurar pesquisando na internet e usando ferramentas como chatGPT e Gemini para tentar reproduzir o design no Figma, o projeto inteiro está nesse repositório no Github, boa jornada!

Para praticar: https://www.hackingwithswift.com/100/swiftui

--

--

Lucas Dantas
Apple Developer Academy | UFPE

iOS Development Student @ Apple Developer Academy | Computer Science Undergraduate @ UFPE