0% found this document useful (0 votes)
119 views22 pages

Swift Bootcamp

Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1/ 22

TOC

--------------------
1~ Ways to format layouts
2~ Inits
3~ Enums
4~ ForEach
5~ ScrollView
6~ LazyVgrid, LazyHGrid, GridItems
7~ Safe Areas
8~ Buttons
9~ @State property wrapper
10~ Extracting Functions & Views
11~ Extract Subviews
12~ @Binding property wrapper
13~ Conditionals
14~ Ternary Operators
15~ Animations
16~ Transitions
17~ Sheets (
18~ Lists and Foreach Statements
19~ NavigationStack
20~ Alerts
21~ confirmationDialogue
22~ Adding commas to number
23~ ContextMenu

------------------------------------------------------------------------

1~ Ways to format layouts


----------------------------------------------
Padding
Spacing
Stacks (Hstack, Vstack, Zstack)
Frames

2~ Inits
----------------------------------------------
Basically an initial function that runs as soon as view is created.

Swift is smart in that it creates an initializer at the end of the code so


you can see your creation. That's why if you have variables outside the
body of your code but it isn't concretely defined, you have to pass
those variables/parameters into the initializer function at the end of
the code (gets deleted when you compile the code). Basically like the
swift view file is a function and it's saying that it needs these
parameters to do anything.

Can tie things together within init so that you don't have to pass every
parameter
each time you initialize

CODE:

import SwiftUI

struct Title: View {

let backgroundColor: Color


let count: Int
let title: String

init(backgroundColor: Color, count: Int, title: String) {


self.backgroundColor = backgroundColor
self.count = count
self.title = title
}

becomes

import SwiftUI

struct Title: View {

let backgroundColor: Color


let count: Int
let title: String

init(count: Int, title: String) {


self.count = count
self.title = title
if title == "Apples" {
self.backgroundColor = .red
} else {
self.backgroundColor = .orange
}
}

3~ Enums
-----------------------------------------------

Allow you to create cases, so that each time a category is mentioned,


you have only said choices.

CODE:

import SwiftUI

struct Title: View {

let backgroundColor: Color


let count: Int
let title: String

init(count: Int, fruit: Fruit) {


self.count = count
if fruit == .apple {
self.title = "Apples"
self.backgroundColor = .red
} else {
self.title = "Oranges"
self.backgroundColor = .orange
}
}
enum Fruit {
case apple
case orange
}

in the context of Alerts:

@State var alertType: MyAlerts? = nil

enum myAlerts {
case success
case error
}

//after body
func getAlert() -> Alert {
switch alertType {
case .error:
return Alert(title: Text(“There was an error!”))
case .success:
return Alert(title: Text(“Success!”), message: nil,
dismissButton:
.default(Text(“OK”), action: {
backgroundColor = .green
}))
default:
return Alert(title: Text(“Error”))

}
}

** can add as many cases as there are enums, in your code you will
call the getAlert function (which replaces the Alert() part of the .alert
function. You will control which mmessage pops up by setting the
alertType (part of your function) with:
alertType = .success or alertType = .error
In the example from teh vbideo, he had two buttons embedded in a
Vstack, the content of the buttons set which enum you want
(alertType) and toggled the showAlert boolean (which brings up the
actual popup). then after the vstack you call the .alert function which
contains your cleaned code function getAlert() which contains the
Alert() portion.

REMEMBER
@State var alertType: myAlerts? = nil

alerttype is the name of the variable, and it is of type enum (defined as


MyAlerts?). so when you are setting the value of alertType, you are
setting it equal to the case you want.
4~ ForEach
-------------------------------------------------

CODE:

// Creates a vertical stack of 99 circles (goes off screen, will need


scroll view)
VStack {
ForEach(0..<100) {index in
CIRCLE()
.FRAME(HEIGHT: 50)
}
}

OR

// creates a verticle stack of the things in the array data


let data: [String] = ["hi", "hello", "hey everyone"]
let myString: String = "hello"

var body: some View {


VStack {
ForEach(data.indices) { index in
Text("\(data[index]): \(index)"
}
}
}
5~ ScrollView
---------------------------------------------------------
embed VStack or HStack in Scrollview {}
-use LazyVStack or LazyHStack so that only the ones shown on
screen are loaded (better for memory)
theres one with more parameters where you can turn off indicator

6~ LazyVgrid, LazyHGrid, GridItems


---------------------------------------------------------

CODE:

let columns: [GridItem] = [


GridItem(.fixed (50), spacing: nil, alignment: nil),
GridItem(.fixed (50), spacing: nil, alignment: nil),
GridItem(.fixed(100), spacing: nil, alignment: nil),
GridItem(.fixed (50), spacing: nil, alignment: nil),
GridItem(.fixed(50), spacing: nil, alignment: nil),
]

var body: some View {


LazyVGrid(columns: columns) {
ForEach (0. <50) { index in
Rectangle ()
.frame (height: 50)

}
}
}

** each GridItem is a column, so because 3rd is 100 instead of 50, it is


wider

** GridItem can also take .flexible and .adaptive


- flexible will adjust to fit the width of screen depending on number of
columns
- adaptive takes miniumum and maximum, so will try to fit as many in
the colum as long as they are within the width parameters
In LazyVGrid you can add a section which can have a head/footer.

CODE:

LazyvGrid(
columns: columns,
alignment: center,
spacing:
pinnedViews: [.sectionHeaders ]
content: {
Section (header:
Text("Section 1")
• foregroundColor (.white)
•font (.title) •frame (maxWidth: •infinity, alignment:
•leading)
.background (Color.blue)
•padding ()
){

7~ Safe Areas
------------------------------------------------------------------------

.ignoreSafeArea(edges: .top/.bottom/.all) ** or can leave blank

if you want to call on color for some reason you need

Color.black
.edgesIgnoringSafeArea(.all)

8~ Buttons
------------------------------------------------------------------------

@State var title: String = "This is my title"

var body: some View {


Button("press me") {
self.title = "Button Was pressed"
}
}

or can take an action and a label, seems to better if you really wanna
customize the button with shapes and capsules

/* @State is a property wrapper that basiclaly means that the variuable


can change and that swiftUI should keep checking to see if it does and
adjust the view accordingly */
@State var title: String = "This is my title"

var body: some View {


Button(action:) {
self.title = "Button Was pressed"
}, label: {
Circle ()
.fill (Color.white)
.frame (width: 75, height: 75)
.shadow (radius: 10)
.overlay(
Image(systemName: "heart.fill")
.font (. largeTitle)
.foregroundColor(Color(literal))
)
})
}

9~ @State property wrapper


------------------------------------------------------------------------
/* @State is a property wrapper that basiclaly means that the variuable
can change and that swiftUI should keep checking to see if it does and
adjust the view accordingly */

@State var backgroundColor: Color = Color.green


@State var myTitle: String = "My Title"

var body: some View {


ZStack {
// background
backgroundColor
.ignoringSafeArea()

// content
VStack(spacing: 20) {
Text (myTitle)
.font (.title)
Text ("Count: 1")
.font (.headline)
.underline()
HStack(spacing: 20) {
Button ("BUTTON 1') {
backgroundColor = .red
myTitle = "BUTTON 1 was pressed"
}
Button ("BUTTON 2') {
backgroundColor = .purple
myTitle = "BUTTON 2 was pressed"
}

10~ Extracting Functions & Views


------------------------------------------------------------------------

Basically wherever you can separate out code to make it look cleaner,
do that.

For example if there is some complex set of actions that happens when
a button is pressed, you can put those actions in a function and then
just call that function in the call of the button.

Also... you can seoarate out background and content in the body

so you end up with a body that lookes like this:

var body: some View {


ZStack {
//background
backgroundColor
.edgesIgnoringSafeArea.all)
// content
contentLayer
}
}

var contentLayer: some View {


//content layer specifications

11~ Extract Subviews


------------------------------------------------------------------------
handy for reusing same views across app. basically the same as
creating a separate swift view file, except usually used when it is less
comples.

you click on the code and select extract subview


- creates a new struct that you can reference in your main view struct
with any parameters.

For example you can create an Hstack in the content layer that
refernces the new struct which has info for a square with a certain
backgroiund color, count, and string, and you can cleanly reference
that struct 3 time sto create a horizontal array of 3 squares

12~ @Binding property wrapper


------------------------------------------------------------------------
Connects @State wrapper from parent view to child view

Lets say you have a main view with a state variable background color
that might change
Then you have a subview but wnana change color of parent view
do that by declaring variable with binding variable

For example... you have a backgroundColor that gets changed by a


button. For clean code, you make the button view a separate struct.
Your subview will try to change the declared backgroundcolor but cant
because this subview has no idea what's going on in the parent view.
You have to bind them together so that when you reference changing
backgroundColor, it knows that youre talking about the variable in
main. You will call that button view struct in main, and reference the
variables in the subview (with a $)

CODE:

struct BindingBootcamp: View


@State var backgroundColor: Color = Color.green

var body: some View {


ZStack {
backgroundColor
.edgesIgnoringSafeArea(.all)
ButtonView(backgroundColor: $backgroundColor)
}
}
}

struct ButtonView: View {


@Binding var backgroundColor: Color

var body: some View {


Button(action: {
backgroundColor = Color.orange
}, label: {
Text ("Button" )
.foregroundColor (.white)
.padding ()
.padding (.horizontal)
.background (Color.blue)
.cornerRadius(10)

13~ Conditionals
------------------------------------------------------------------------

CODE:

//Shows a loading icon (ProgressView()) when a conditional is true via


a toggle. You can toggle a conditional variable by attaching .toggle() to
the end of it in your code

import SwiftUI

struct ConditionalBoot: View {

@State var showCircle: Bool = false


@State var isLoading: Bool = false

var body: some View {


VStack (spacing: 20) {
Button("IS LOADING: \(isLoading.description)"){
isLoading.toggle()
}
if isLoading {
ProgressView()
}
Spacer()

}
}
}

14~ Ternary Operators


------------------------------------------------------------------------
These are basically if...else statements but in one line.
- more efficient
- cleaner

condition ? value_if_true : value_if_false

examples:

.fill (isStartingState ? Color.red: Color.blue)

.frame (
width: isStartingState ? 200 : 50,
height: isStartingState ? 400 : 50)
Text (isStartingState ? "STARTING STATE!!! " : "ENDING STATE.")

if showCircle == true {
Circle()
.frame(width:100, height:100)
} else {
Rectangle()
.frame(width:100, height:100)
}

becomes

showCircle ?
AnyView(Circle().frame(width: 100, height: 100))
: AnyView(Rectangle().frame(width: 100, height: 100))

15~ Animations
------------------------------------------------------------------------
Sure, here are two versions of the code that you asked for, one using
`withAnimation` and one using the `.animation` modifier:

Using `withAnimation`:

```swift
//Creates a rectangle that spins and moves when touched

struct ContentView: View {

@State private var isRotated = false

var body: some View {


VStack {
Spacer()

Rectangle()
.fill(Color.blue)
.frame(width: 50, height: 50)
.rotationEffect(.degrees(isRotated ? 360 : 0))
.offset(
CGSize(
width: isAnimated ? Int.random(in: -
100...100) : 0,
height: isAnimated ? Int.random(in: -
100...100) : 0
)
)

Spacer()

Button("Rotate") {
withAnimation {
isRotated.toggle()
}
}
}
}
}
```
//Creates Two rectangles that spin and move independently when
touched

VStack {
Spacer()
Rectangle()
.fill(Color.yellow)
.frame(width: 50, height: 50)
.rotationEffect(.degrees(isAnimated1 ? 360 : 0))
.offset(rectOffset1)
.onTapGesture {
withAnimation {
isAnimated1.toggle()
rectOffset1 = isAnimated1
? CGSize(
width: Int.random(in: -100 ... 100),
height: Int.random(in: -100 ... 100)
)
: .zero
}
}
Spacer()
Rectangle()
.fill(Color.blue)
.frame(width: 50, height: 50)
.rotationEffect(.degrees(isAnimated2 ? 360 : 0))
.offset(rectOffset2)
.onTapGesture {
withAnimation {
isAnimated2.toggle()
rectOffset2 = isAnimated2
? CGSize(
width: Int.random(in: -100 ... 100),
height: Int.random(in: -100 ... 100)
)
: .zero
}
}
Spacer()
}
}

You can customize the animation curves with the following code:

withAnimation(Animation.spring(response: 0.5, dampingFraction: 0.8,


blendDuration: 1.0)) {
isAnimated2.toggle()
rectOffset2 = CGSize(
width: Int.random(in: -rightMost ...
rightMost),
height: Int.random(in: -topMost ...
topMost)
)
}

16~ Transitions
------------------------------------------------------------------------
import SwiftUI

struct SwiftUIView: View {

@State var showView : Bool = false

var body: some View {

ZStack(alignment: .bottom){
VStack {
Button(showView ? "Close Form" : "Show Form"){
withAnimation{
showView.toggle()
}
}
Spacer()
}

if showView {
withAnimation(.easeInOut){
RoundedRectangle(cornerRadius: 30)
.frame(height: UIScreen.main.bounds.height *
0.5)

.transition(.asymmetric(insertion: .move(edge: .leading),


removal: .move(edge: .bottom))
)
}

}
.ignoresSafeArea(edges: .bottom)

}
}

17~ Sheets
------------------------------------------------------------------------
1. need binding sheet variable and need to bind it to the sheet and
need to present second screen
@State var showSheet: Bool = false

/after first struct create a boolean


@State var showSheet : Bool = false
//within view
.sheet(isPresented: $showSheet, content: {
secondScreen()
})

2. need to create that second screen

struct SecondScreen: View {

//dismisses sheet
@Environment(\.presentationMode) var presentationMode

var body: some View {


ZStack(alignment: .topLeading) {
Color.red
.edgesIgnoringSafeArea(.all0)
Button(action: {
presentationMode.wrapperValue.dismiss()
}, label: {
Image(systemName: "xmark")
.foregroundColor(.white)
.font(.largeTitle)
.padding(20)
})
}
}
}

18~ Lists and Foreach Statements


------------------------------------------------------------------------
List {
ForEach(bgColors, id:\.self){bgCOlor in
Navigationlink {
bgColor
.frame(maxWidth: .infinity, maxHeight: .inifinity)
.edgesIgnoringSafeArea(.all)
} label: {
Text(bgColor.description)
}
}
}

Also takes a section function which adds a section. General format is:
Section(
header: Text(“Fruits”)) {
code
}

modifiers

.listStyle()
.listRowBackground(zColor.blue)
.accentColor(.purple)

19~ NavigationStack
------------------------------------------------------------------------

NavigationStack {
List {
ForEach(bgColors, id:\.self){bgCOlor in
Navigationlink {
bgColor
.frame(maxWidth: .infinity, maxHeight: .inifinity)
.edgesIgnoringSafeArea(.all)
} label: {
Text(bgColor.description)
}
}
}
}

modifiers
Move
.onMove(perform: {indices, newOffset in
bgColor.move(fromOffsets: IndexSet, toOffset: Int)
})

becomes
.onMove(perform: {indices, newOffset in
bgColor.move(fromOffsets: indices, toOffset: newOffset)
})

becomes
.onMove(perform: move)

+
func move(indices: IndexSet, newOffset: int) {
bgColor.move(fromOffsets: indices, toOffset: newoffset)
}

Delete
.onDelete(peform: {indexSet in
bgColor.remove(atOffsets: IndexSet)
})

becomes
.onDelete(peform: {indexSet in
bgColor.remove(atOffsets: indexSet)
})

becomes
.onDelete(perform: delete)
+
func delete(indexSet: IndexSet) {
bgColor.remove(atOffsets: indexSet)
}

Add
**for this we are going to add a button to the navigationbar

.navigationBarItems (
leading:
EditButton(),
trailing:
Button("add", action: {
add()
})
)

func add() {
bgColors.append(.purple)
}

20~ Alerts
------------------------------------------------------------------------
.alert (isPresented: $someBan, content: {
Alert(
title: (text),
message: (Text?),
primaryButton: (Alert. Button),
secondaryButton: (Alert. Button))
})

You can also extract the Alert() part to clean up code by putting that
inside a function:
funct getAlert() -> Alert {
return Alert(
title: (text),
message: (Text?),
primaryButton: (Alert. Button),
secondaryButton: (Alert. Button))
})

21~ confirmationDialogue
------------------------------------------------------------------------
.confirmationDialog("Hello", isPresented: $ellipsisButton, actions: {
Button("Blue", role:.destructive){
backgroundColor = .blue
}
Button("Black"){
backgroundColor = .black
}
Button("Red"){
backgroundColor = .red
}
Button("Yellow"){
backgroundColor = .yellow
}
})

similar to a sheet. You .confirmation dialogue, and inside perentheses you put the title, some
boolean that toggles it, and then actions inside some brackets (this is where you put all the
buttons you want in there. You can modify the buttons, for example if you set the role to
destructive it will be red.

22~ Adding commas to numbers


------------------------------------------------------------------------

@Binding var likesCount : Int


var formattedLikesCount: String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter.string(from: NSNumber(value: likesCount)) ?? "\(likesCount)"
}

what the ?? does is default to an unformatted string of likesCount if it


somehow fails

23~ ContextMenu
------------------------------------------------------------------------
a modifier you can add to an image or anything really that brings up a
menu when long presseed

takes a menuItems parameter, in which you would just put whatever


buttons you want

Image(systemName: bookmarkTapped ? "bookmark.fill" : "bookmark")


.foregroundColor(bookmarkTapped ? .yellow : .black)
.font(.system(size: 25))
.padding(.trailing)
.contextMenu(menuItems: {
Button(action: {bookmarkTapped = false},
label: {
Label("Remove", systemImage: "bookmark.slash")
.accentColor(.black)
})
})
.onTapGesture {
bookmarkTapped.toggle()
}

You might also like