/* These notes are stream of consciousness and the code for each note in total is
not intended to be ran for any practical purpose. The goal is to help make sense
of what is happening in the body of an app project, and the notes are written by
a complete coding beginner (me), intended to be used by a beginner (me), and
likely as a result lack the experience / accuracy of term usage that would be
prominent in the works of someone more experienced. With that in mind, I hope
that if you are a beginner like me and are learning Swift that these notes
are as helpful to you reading them as they have been to me writing them.
Alons-y! */
If you find this helpful, send me a cup of tea. :)
https://ko-fi.com/guntherhammel
*** The first entry for each topic will likely have an explanation (as I understood it at the time) of the topic in general, and later entries will likely have more intricate or complicated code.
Alerts, message:
Background (.background, .ultraThinMaterial)
Button (buttonStyle, tint, action:, } label {, .destructive, Label, .cancel)
Color (color.secondary, custom colors)
Date, Time, Date Picker, Date Modifiers
@FocusState (.isFocused)
ForEach
.foregroundStyle
Form
.frame
Gradients (Linear, stops:, radial, angular/conical
HStack
Image (systemName:, decorative, resizable, scaledToFit, .clipShape, .shadow)
.ignoreSafeArea
.keyboardType
Modifier Extensions
Navigation Stack
navigationTitle (and DisplayMode)
.padding
Preview
Picker (.pickerStyle)
.self
Section (and header text)
Spacer
@State
Stepper
Switch
TextField and Text (.font, .subheadline, .semibold)
.toolbar
Views and View Modifiers (custom modifiers, view extensions)
VStack
ZStack
back to top
Button ( " Show alert " ) {
// creates button with text on it
displayingAlert = true
// when button is pressed, displayingAlert is toggled to true
}
. alert ( " important message! " , isPresented : $displayingAlert ) {
/* creates an alert with text "important message!", and it set as part of
a two-way binding to the value of displayingAlert, which means that
once "Dismiss" is hit, the value of displayingAlert goes back to false */
Button ( " Dismiss " ) { }
/* makes button to dismiss the alert, and there is no code that gets ran
when the button is pressed. tapping that button, as a side effect, is what
ends up setting the value of displayingAlert back to false due to the
two-way binding. */
Button ( " Cancel " , role : . cancel ) { }
// .cancel makes text slightly bolded (safe option)
Button ( " Erase " , role : . destructive ) { }
// .destructive makes text red
} message : {
// displays a subtext message on the alert.
Text ( " Please review carefully " )
}
. alert ( " Critcal \( rollSuccess ) You've rolled a nat \( diceRoll ) " , isPresented : $showingCrit ) {
Button ( " Dismiss " ) { }
} // display alert, updates showingCrit bool state
Background (.background, .ultraThinMaterial)
back to top
. background ( . ultraThinMaterial )
//applies some frosted glass / blurring to this text window
}
. background ( . green )
/* applies green color to entire screen, but the .frame line above
trims the background to fill only the designated area*/
. background ( . indigo
. gradient )
// applies an indigo background with a very subtle indigo gradient
. background ( . thinMaterial )
// makes the frame a little more seethrough
Button (buttonStyle, tint, action:, } label {, .destructive, Label, .cancel)
back to top
Button ( " Tap Count: \( tapCount ) " ) {
/* creates a button with text, and inserts the current tapCount variable value */
tapCount += 1
/* increases tapCount by 1 */
}
Button ( " Done " ) {
amountIsFocused = false
/* creates a toolbar item to the navigation stack. If user has the amount field
selected and is typing into the field, there will be a "Done" button in the top
right corner of the screen. When the button gets pressed, amountIsFocused changes
back to false, which dismisses the done button and the keyboard */
}
Button ( " Press this other button... " , role : . destructive , action : displayMessage )
/* makes another button which runs a function called
by the action command from within the button parentheses.
** this function is stored outside the bottom of the view body
.destructive turns the text red, to be used for marking a button
as dangerous */
Button ( " Button 3 " ) { }
. buttonStyle ( . borderedProminent )
. tint ( . green )
. foregroundStyle ( . black )
/* makes the background more pronounced and changes the
color of the button border, while changing the text to be black */
Button ( " Button 4 " , role : . destructive ) { }
. buttonStyle ( . borderedProminent )
// makes the destructive button bordered and more pronounced
Button {
// makes a custom button
print ( " You're good at button-presssing! " )
// tells the button what to do when it is pressed
} label : {
// starts section for entering in descriptive info about the button
Text ( " Tap me... " )
// this is what the button displays on it
. padding ( )
// makes the 'button' area larger (really it expands the space around the text)
. foregroundStyle ( . black )
// makes the text black
. background ( . purple )
// makes the 'button' purple
}
Button ( " Save " , systemImage : " externaldrive.fill.badge.plus " ) {
// creates custom button using system image as tappable item
print ( " Saving... " )
}
Button {
print ( " Doing the thing... " )
} label : {
Label ( " Save " , systemImage : " externaldrive.fill.badge.plus " )
/* another way of doing the same thing as the previous button,
but this gives the ability to further customize/format the button */
. padding ( )
// gives space around text of button
. foregroundStyle ( . white )
// turns text white
. background ( . blue . gradient )
// gives button color a subtle gradient
}
Button ( " Show alert " ) {
// creates button with text on it
displayingAlert = true
// when button is pressed, displayingAlert is toggled to true
}
. alert ( " important message! " , isPresented : $displayingAlert ) {
/* creates an alert with text "important message!", and it set as part of
a two-way binding to the value of displayingAlert, which means that
once "Dismiss" is hit, the value of displayingAlert goes back to false */
Button ( " Dismiss " ) { }
/* makes button to dismiss the alert, and there is no code that gets ran
when the button is pressed. tapping that button, as a side effect, is what
ends up setting the value of displayingAlert back to false due to the
two-way binding. */
Button ( " Cancel " , role : . cancel ) { }
// .cancel makes text slightly bolded (safe option)
Button ( " Erase " , role : . destructive ) { }
// .destructive makes text red
} message : {
// displays a subtext message on the alert.
Text ( " Please review carefully " )
}
Button ( " Continue " , action : askQuestion )
} message : {
Text ( " Your score is ??? " )
/* creates button within alert which says "Continue",
shows a message displaying current score and calls the
askQuestion method when "Continue" button is tapped*/
}
Button {
// Flag was tapped
flagTapped ( number )
/* sets up button to track which number flag is tapped
and compare it against the correct answer*/
Button { // level up button, can't go past 5
if level < 5 {
level += 1
} else {
displayMaxLevelAlert = true
} // displays alert message if trying to level up past 5
} label : {
Text ( " Level Up ⬆️ " )
. frame ( maxWidth : 150 , maxHeight : 50 ) // body of button
. foregroundStyle ( . white ) // text made white
. background ( . secondary ) // color to button itself
. clipShape ( . capsule ) // shape of button clipped from frame
}
. alert ( " Max level reached. You have learned all I can teach you... " , isPresented : $displayMaxLevelAlert ) {
Button ( " Fine... " ) { }
}
Button {
diceRoll = Int . random ( in : 1 ... 20 )
if ( diceRoll + updateStatMod ( statName : selectedStat ) ) >= 10 {
rollSuccess = " Success! "
} else {
rollSuccess = " Failure! "
} // define whether success / failure is displayed
if diceRoll == 1 || diceRoll == 20 {
showingCrit = true
} // define critical fail / hit values, and displays alert if rolled
rollResultMessage = """
You rolled a \( diceRoll ) ,
+ \( updateStatMod ( statName : selectedStat ) ) makes \( diceRoll + updateStatMod ( statName : selectedStat ) )
"""
} label : { // start manipulations to button
Text ( " Make a \( selectedStat ) check " )
. frame ( maxWidth : 200 , maxHeight : 40 )
. font ( . subheadline )
. foregroundStyle ( . black )
. background ( . secondary )
. clipShape ( . capsule )
. padding ( )
}
Color (color.secondary, custom colors)
back to top
Color . secondary
/* fills entire space with slightly transparent black to
give some shading abilities to other colors
** this Color.secondary could also be written as Color.colorName
(red, blue, green, purple, etc
- in the event we were to write the color in that way, we wound
remove the .background line of code from line 32
Can also make customer colors with the following syntax -
Color(red: 1, green: 0.8, blue: 0)
to tell swift to mix ratios of each of the RGB colors
*/
back to top
func body ( content : Content ) -> some View {
// this line can be shortcut-entered by typing 'body' and selecting second option
content
. font ( . largeTitle )
. foregroundStyle ( . white )
. padding ( )
. background ( . blue )
. clipShape ( . rect ( cornerRadius : 10 ) )
} /* takes the Content from input and passes it into the func
which in this case applies modifiers to it */
func body ( content : Content ) -> some View { /* type body, select second option; used since views are involved
and we don't want to specify which type just yet */
ZStack ( alignment : . bottomTrailing ) { // places in the bottom right area of view
content
/* makes new ZStack (stackable view) with the content passed into the
struct at the callsite. */
Date, Time, Date Picker, Date Modifiers
back to top
@ State private var wakeUp = Date . now
// creates new state variable for date equal to current date
DatePicker ( " Please enter a date " , selection : $wakeUp )
/* creates date picker which ties the user input to state of wakeUp */
DatePicker ( " Please enter a date " , selection : $wakeUp , displayedComponents : . hourAndMinute )
// shows just hours and minutes
. labelsHidden ( )
/* hides label from display but still keeps it accessible for
anyone using screen reader or accessibility features */
DatePicker ( " Please enter a date " , selection : $wakeUp , displayedComponents : . date )
// shows just the date
DatePicker ( " Please enter a date " , selection : $wakeUp , in : Date . now ... )
/* creates an open-ended date range which makes all future dates
available for user to select, but makes all past dates and times unavailable */
Text ( Date . now , format : . dateTime . hour ( ) . minute ( ) )
// displays text on the screen from today in the format of hour and minute
Text ( Date . now , format : . dateTime . day ( ) . month ( ) . year ( ) )
/* displays text on the screen from today in the format of day, month, year.
***** With this method, we are asking for the data, not arranging it ourselves;
swift takes our request and formats it for us automatically based on user
preference */
Text ( Date . now . formatted ( date : . long , time : . shortened ) )
// displays text on screen with longer month name and minimized time display
func exampleDates ( ) { // this is the manual way to do it, don't default to this method
let now = Date . now
// defines constant as current date.time
let tomorrow = Date . now . addingTimeInterval ( 86400 )
// creates constant for point in time 86400 sec later than now (~ 1 day) in the futre
let range = now ... tomorrow
/* creates a constant which defines a range of time between now and 86400
sec from now (~ 1 day range) */
}
func exampleDatesButEasier ( ) { // this is the better way, default to doing it this way
// use swift components to do all the date/time calculations automatically
// var components = DateComponents()
// components.hour = 8
// components.minute = 0
// let date = Calendar.current.date(from: components) ?? .now
/* sets date to be 8 AM on any given day, and sets backup option
to be the current date/time vial nil coalescing */
let components = Calendar . current . dateComponents ( [ . hour , . minute ] , from : . now )
/* sets a constant which holds the hour and minute of a given day. In
this example, it is using the current date / time */
let hour = components . hour ?? 0
let minute = components . minute ?? 0
/* creates two constants derived from components constant, using the .hour
and .minute elements to pull data and store separately. Uses nil coalescing
to set backup options since swift wants them and is needy */
back to top
@ FocusState private var amountIsFocused : Bool
/* this will help with hiding the keyboard later in the code;
we are telling Swift that the user currently has this variable focused
if true */
. focused ( $amountIsFocused )
/* adds a text field to this section, tying the value that
will be drawn to the screen to be equal to the checkAmount
variable by adding the $ in front of the variable name. Since
TextField likes to only have strings typed into it, we can
weasel our way into making swift happy by using format: .currency
and tell Swift to use that input type instead of typing an Int.
after code:, this line of code checks the current locale of the
user, checks to see if there is a currency tied to that region,
and if thereis then it uses that currency type, otherwise it defaults
to USD
the .keyboard extension forces the numberpad keyboard to show
instead of the text keyboard.
the .focused extension toggles amountIsFocused to be true
*/
if amountIsFocused {
Button ( " Done " ) {
amountIsFocused = false
/* creates a toolbar item to the navigation stack. If user has the amount field
selected and is typing into the field, there will be a "Done" button in the top
right corner of the screen. When the button gets pressed, amountIsFocused changes
back to false, which dismisses the done button and the keyboard */
}
}
@ FocusState private var inputIsFocused : Bool
// sets up the states for input/output distance unit types and user input value
. focused ( $inputIsFocused )
//when entering here, inputIsFocused = true
back to top
ForEach ( 0 ..< 100 ) {
/* creates a list of items for range mentioned as parameter.
Very useful as a picker feature where we want to present user
with a range of options to be selected*/
Text ( " Row \( $0 ) " )
/* determines the screen output to be displayed for each row.
Uses the $0 trailing closure syntax to print out the number
of the row for each iteration through the loop.*/
}
ForEach ( members , id : \. self ) {
/* uses the elements within array members as avaialble
selections for the picker window, with each picker
option id being its own String as it is stored in the array */
Text ( $0 )
/* for each element in the array numbers, show as text
the value stored in that location of the array - this
is what the $0 is telling Swift to do */
}
ForEach ( 2 ..< 100 ) {
Text ( " \( $0 ) people " )
/* creates a picker (dropdown menu) in this section which
updates the state of the variable numberOfPeople to be whatever
the user selects. The picker will have options for 2 - 99
people, and uses string interpolation to print the element value
of the range iterated through ForEach */
ForEach ( tipPercentages , id : \. self ) {
Text ( $0 , format : . percent )
/* uses the values in array tipPercentages (id = the
actual values foe each element of the array) and
draws text on the screen using each of those values listed as
% amounts */
}
ForEach ( unitOptions , id : \. self ) {
Text ( $0 )
//each item shows the text value of the corresponding array element
}
back to top
. foregroundStyle ( . secondary )
//makes slightly transluscent
. foregroundStyle ( . orange )
// turns the foreground (in this case, the text) orange
. foregroundStyle ( . black )
/* makes the background more pronounced and changes the
color of the button border, while changing the text to be black */
. foregroundStyle ( rarityTextColor ( rarityOfItem : rarityInput ) )
} /* creates housing for function which applies color change
of text based on item rarity */
back to top
Form {
/* Form is what creates the interactive items on the screen */
back to top
. frame ( minWidth : 200 , maxWidth : . infinity , maxHeight : 200 )
/* gives restrictions for which area of the screen should have
the green background. */
. frame ( maxWidth : . infinity , maxHeight : . infinity )
// creates a screenwide frame for this text element
. frame ( minWidth : 100 , maxWidth : 250 , maxHeight : 200 )
// gives the image dimensions to occupy
. frame ( maxWidth : . infinity )
// stretches a frame edge to edge horizontally aroung flags
Gradients (Linear, stops:, radial, angular/conical
back to top
LinearGradient ( colors : [ . blue , . green ] , startPoint : . top , endPoint : . bottom )
/* makes a background covering the safe area with colors listed in
an array, with designated start and end points */
LinearGradient ( stops : [
. init ( color : . purple , location : 0.20 ) ,
. init ( color : . red , location : 0.90 )
] , startPoint : . top , endPoint : . bottom )
/* this array makes a sharper contrast in the colors, beginning to
mix into red 20% from the top of the screen, with red starting to
take over around 90% from the top of the screen, and tells the gradient to
start from the top of the screen while ending at the bottom */
RadialGradient ( colors : [ . orange , . black , . purple ] , center : . center , startRadius : 20 , endRadius : 200 )
/* makes a radial gradiant centered on the middle of the screen, expanding outward
based on the start/end radii values entered */
AngularGradient ( colors : [ . red , . orange , . yellow , . green , . blue , . indigo , . red ] , center : . center )
/* makes a sweeping gradient with center point on middle of screen,
with colors changing on a line from center of screen to edge of device,
changing from left to right */
. background ( . blue . gradient )
// gives button color a subtle gradient
LinearGradient ( stops : [
. init ( color : Color ( red : 0.1 , green : 0.2 , blue : 0.45 ) , location : 0.3 ) ,
. init ( color : Color ( red : 0.9 , green : 0.4 , blue : 0.2 ) , location : 0.5 )
] , startPoint : . top , endPoint : . bottom )
. ignoresSafeArea ( )
/* Makes a ZStack for putting things on top of things,
makes the back stack a gradient and tells the color
to ignore the safe area, which colors all the way to the edges
of the disoplay */
back to top
HStack {
//starts a horizontal stack
Spacer ( )
//adds room to the left of the HStack
VStack ( spacing : 20 ) {
//starts a vertical stack (left column) inside the horizontal stack
/* can add to parentheses for VStack things like (alighment: leading) or (alignment: trailing)
to have the text aligned left or right respectively.
Can also add (spacing: X) to give more space between lines
***Swift will automatically fill the space with the content */
Image (systemName:, decorative, resizable, scaledToFit, .clipShape, .shadow)
back to top
Image ( systemName : " pencil.circle " )
// adds an image to the display with filename matching string
/* There is a whole catalog of icons that can be used in xcode when
building apps. Free catalog downloadable: https://developer.apple.com/sf-symbols/ */
Image ( decorative : " cat " )
// adds an image from assets catalogto the display with filename matching string.
// the decorative bit keeps the image from being read out on things like VoiceOver
. resizable ( )
// allows for the image to be resized
. scaledToFit ( )
// keeps the image from getting squished by other elements
Image ( countries [ number ] )
// sets the button image to be the country's flag
. clipShape ( . capsule )
// masks the flag into a rounded oval shape
. shadow ( radius : 5 )
// applies shadow to the button
. clipShape ( . rect ( cornerRadius : 30 ) )
// rounds the edges of the frame
back to top
. ignoresSafeArea ( )
/* tells swift to ignore the upper and lower horizontal limits
of the safe area when filling the screen with the background
color. IMPORTANT: make sure all user-interactive elements of
your code live and are visible in the safe area, otherwise
there will be issues with getting to those items (might be behind clock,
dynamic island, home line at bottom of screen etc) */
back to top
. keyboardType ( . decimalPad )
. focused ( $amountIsFocused )
/* adds a text field to this section, tying the value that
will be drawn to the screen to be equal to the checkAmount
variable by adding the $ in front of the variable name. Since
TextField likes to only have strings typed into it, we can
weasel our way into making swift happy by using format: .currency
and tell Swift to use that input type instead of typing an Int.
after code:, this line of code checks the current locale of the
user, checks to see if there is a currency tied to that region,
and if thereis then it uses that currency type, otherwise it defaults
to USD
the .keyboard extension forces the numberpad keyboard to show
instead of the text keyboard.
the .focused extension toggles amountIsFocused to be true
. keyboardType ( . decimalPad )
//forces numeric keyboard
back to top
extension View { // new extension on the view protocol
func watermarked ( with text : String ) -> some View {
modifier ( Watermark ( text : text ) )
// creates new modifier with the functions of the Watermark struct made above
}
}
extension View { // creates an extension of the View protocol for us to use later
func titleStyle ( ) -> some View {
modifier ( Title ( ) )
} /* creates a function within the extension which will run, as a modifier, the
Title struct without having to type .modifier(insertStructNameHere())*/
}
Text ( " Hello, world! " )
. modifier ( Title ( ) )
/* applies Title() function to the text view, which in this case
takes the content from the text field (the string), passes it
into the function called 'body', which takes the above content
and applies all the modifiers in the function to it */
Text ( " Hello again! " )
. titleStyle ( )
/* runs the same custom modifier as is run on Hello, World, but
without having to state that it is a modifier first thanks to
the extension made above */
Text ( " This is intellectual property! " )
. titleStyle ( )
. watermarked ( with : " don't steal it " )
/* applies titlestyle and watermarked extensions to text */
extension View { // new custom extension for View type.
func applyStyle ( ) -> some View { // generic function call returning some kind of view.
modifier ( CardStyle ( ) ) /* this command makes a new custom extension which runs
the code in the CardStyle struct */
VStack { // outermost VStack
strengthStat // HStack 1
. modifier ( CardStyle ( ) )
/* calls all three of the stat views from CardStyle struct,
and applies the styling to each without having to type the code
from CardStyle struct repeatedly */
dexterityStat // HStack 2
. modifier ( CardStyle ( ) )
// same note as strengthStat
WisdomStat // HStack 3
. applyStyle ( )
/* this uses the custom View extension created above with applyStyle function
instead of having to specify the .modifier modifier */
extension View {
func rarityStyled ( for rarity : itemRarity ) -> some View {
modifier ( RarityStyle ( rarityInput : rarity ) )
} // shortens RarityStyle to an extension on View
back to top
NavigationStack {
/* Allows for our ConventView to be interacted with and scrolled so we don't
have to constantly run the code to open the other preview from Device Hub */
NavigationStack {
/* Allows new views to slide in from left or right as
user enters into or exits out of forms */
navigationTitle (and DisplayMode)
back to top
. navigationTitle ( " SwiftUI " )
/* adds the title to the top of the screen output */
. navigationBarTitleDisplayMode ( . inline )
/* makes the navigation bar title smaller and places it at the
top center of the screen instead of having it be the default
larger font on the left side of the screen */
. navigationTitle ( " WeSplit " )
// gives navigation stack a title at top left corner of display for first page
// *** Important include this line INSIDE the navigation stack instead of outside of it
back to top
. padding ( 50 )
//expands the effect of the foregroundStyle for text
. padding ( . vertical , 40 )
// extends frame vertically slightly
back to top
#Preview {
/* This allows for the code to be drawn to the simulator window so we can see
what the code is going to look like */
back to top
Picker ( " Select your party member " , selection : $selectedMember ) {
/* fills the new form with a picker (dropdown window box
allowing for a selection of multiple items. In this case, the
default selection is the stored variable selectedMember,
and when a new choice is made it will be written in as the new
state for var selectedMember because of the $ in front of
the variable name (two-way binding)) */
Picker ( " Number of people " , selection : $numberOfPeople ) {
ForEach ( 2 ..< 100 ) {
Text ( " \( $0 ) people " )
/* creates a picker (dropdown menu) in this section which
updates the state of the variable numberOfPeople to be whatever
the user selects. The picker will have options for 2 - 99
people, and uses string interpolation to print the element value
of the range iterated through ForEach */
}
}
. pickerStyle ( . navigationLink )
Picker ( " " , selection : $sourceUnit ) {
//use state of sourceUnit for picked item
Picker ( " " , selection : $resultUnit ) {
ForEach ( unitOptions , id : \. self ) {
Text ( $0 )
}
} . pickerStyle ( . segmented )
Picker ( " " , selection : $characterClass ) { // picker for class
ForEach ( classes , id : \. self ) { // uses items from classes array
Text ( $0 ) // shows each item from array as its own text object
}
} . pickerStyle ( . wheel ) // inline scroll to pick class
} // below are edits made to HStack, similar to item group 1
. frame ( maxWidth : . infinity , maxHeight : 100 )
. background ( . ultraThinMaterial )
. clipShape ( . rect ( cornerRadius : 30 ) )
. shadow ( radius : 5 )
Picker ( " Stat " , selection : $selectedStat ) { // bind user pick to selectedStat
ForEach ( statNames , id : \. self ) { // use statNames elements as source
Text ( $0 ) // use each element as option in picker
}
}
. pickerStyle ( . wheel )
back to top
ForEach ( members , id : \. self ) {
/* uses the elements within array members as avaialble
selections for the picker window, with each picker
option id being its own String as it is stored in the array */
Section (and header text)
back to top
Section {
/* Section creates the spaceer area beneath the last item in the group */
Section ( " How much would you like to tip? " ) {
/* adding text to parentheses after the section line adds the text as a subtitle
for the section*/
back to top
Spacer ( )
//adds a space between the top edge of the screen and the first item in the VStack
Text ( " Upper left " )
Spacer ( )
//adds a space between the first and second item in the VStack
Text ( " Left middle " )
Spacer ( )
//adds a space between the second and third item in the VStack
Text ( " Lower left " )
Spacer ( )
//adds a space between the third item in the VStack and the bottom edge of the screen
} //end of first vertical stack
Spacer ( )
//adds a space between the first VStack and second VStack
/* 1 of 4 spacers used to help make the
app look nice on all sizes of screens */
Spacer ( )
Spacer ( )
/* 2 of 4 spacers used to help make the
app look nice on all sizes of screens */
back to top
@ State private var tapCount = 0
/* @State allows for variables sourced from within a struct to be maniplulated
similar to how we need to use mutating func to update variables repated to
a function.
****** @State is designed for simple properties that are stored in one view, so
marking them as private is recommended by Apple to signal they are used and made
only here. */
@ State private var checkAmount = 0.0
@ State private var numberOfPeople = 2
@ State private var tipPercentage = 20
/* sets default values for three values that
are expected to accept input from user during
app usage. It will auto-update the values as changes are made.
Each of these have a sensible starting
value */
@ State private var displayingAlert = false
// sets up for the displayingAlert bool to be monitored by Swift
@ State private var rolledItemName = " "
@ State private var rolledItemRarity = itemRarity . blank
// placeholders for state variables
back to top
Stepper ( " \( sleepAmount . formatted ( ) ) hours " , value : $sleepAmount , in : 4 ... 12 , step : 0.25 )
/* creates stepper block, binds input to state of sleepAmount
with available values between 4 and 12, but not beyond them. when
input is entered, value will change by 0.25 each tap.
Adding .formatted() removes the excess 0s after the decimal point
*/
back to top
switch unitIn {
// reduces user input to milimeters
case " MM " : reducedValue = userInput
case " CM " : reducedValue = userInput * 10
case " M " : reducedValue = userInput * 1000
case " K " : reducedValue = userInput * 1_000_000
case " IN " : reducedValue = userInput * 25.4
case " FT " : reducedValue = userInput * 304.8
case " YD " : reducedValue = userInput * 914.4
case " MI " : reducedValue = userInput * 1_609_344
default : reducedValue = userInput
}
switch unitOut { // converts milimeters to the desired output measurement
case " MM " : outputValue = reducedValue
case " CM " : outputValue = reducedValue / 10
case " M " : outputValue = reducedValue / 1000
case " K " : outputValue = reducedValue / 1_000_000
case " IN " : outputValue = reducedValue / 25.4
case " FT " : outputValue = reducedValue / 304.8
case " YD " : outputValue = reducedValue / 914.4
case " MI " : outputValue = reducedValue / 1_609_344
default : outputValue = reducedValue
}
return outputValue
TextField and Text (.font, .subheadline, .semibold)
back to top
TextField ( " Enter your name " , text : $name )
/* creates a blank text field with prompt "enter your name.
the $ symbol in front of name indicates two-way binding, which means
that swiftUI will know to both read and write changes to the variable
in real time as they are made. This is necessary for any variable
we want to be able to use, reuse, update, and have changes reflected
throughout our code */
Text ( " Your name is \( name ) " )
} /* displays text in new space, with variable name being added as it
is typed - in real time thanks to two-way binding - by the user*/
}
Text ( " Row \( $0 ) " )
/* determines the screen output to be displayed for each row.
Uses the $0 trailing closure syntax to print out the number
of the row for each iteration through the loop.*/
Text ( $0 )
/* for each element in the array numbers, show as text
the value stored in that location of the array - this
is what the $0 is telling Swift to do */
Text ( " \( $0 ) people " )
/* creates a picker (dropdown menu) in this section which
updates the state of the variable numberOfPeople to be whatever
the user selects. The picker will have options for 2 - 99
people, and uses string interpolation to print the element value
of the range iterated through ForEach */
Text ( " \( userInput . formatted ( ) ) \( sourceUnit ) = \( conversion ( unitIn : sourceUnit , unitOut : resultUnit ) . formatted ( ) ) \( resultUnit ) " )
//uses string interpolation calling values from each variable
Text ( " Guess the Flag " )
. font ( . largeTitle . bold ( ) )
. foregroundStyle ( . white )
/* game title font at top of screen */
. font ( . subheadline . weight ( . heavy ) )
/* turns the text (the foreground item) white
and makes adjustments to font styling */
Text ( countries [ correctAnswer ] )
. font ( . largeTitle . weight ( . semibold ) )
// calls correct answer from countries array
// makes adjustments to font styling
. font ( . title . bold ( ) )
/* displays score at bottom of screen */
. font ( . title2 ) // change font size to be slightly bigger
Text ( statSelect ( statName : selectedStat ) , format : . number )
/* run statSelect function, which passes in the key from
statBlock dictionary matching selectedStat string. Returns
value from dictionary that is tied to aformentioned key.
Formats text as a number */
back to top
. toolbar {
if amountIsFocused {
Button ( " Done " ) {
amountIsFocused = false
/* creates a toolbar item to the navigation stack. If user has the amount field
selected and is typing into the field, there will be a "Done" button in the top
right corner of the screen. When the button gets pressed, amountIsFocused changes
back to false, which dismisses the done button and the keyboard */
. toolbar {
if inputIsFocused {
Button ( " Done " ) {
inputIsFocused = false
}
}
back to top
VStack ( spacing : 20 ) {
//starts a vertical stack (left column) inside the horizontal stack
/* can add to parentheses for VStack things like (alighment: leading) or (alignment: trailing)
to have the text aligned left or right respectively.
Can also add (spacing: X) to give more space between lines
***Swift will automatically fill the space with the content */
var body : some View {
/* defaults to a VStack, meaning that all of these gradients
which would, on their own, take up the entirety of the screen
will take up an equal portion of the display, as calculated
by swift for us */
} // end of VStack for group 1 (name, class, level, level up button)
// below are edits made to VStack group 1
. frame ( maxWidth : . infinity , maxHeight : 300 ) // background created
. background ( . ultraThinMaterial ) // background type
. clipShape ( . rect ( cornerRadius : 30 ) ) // shapes the frame
. shadow ( radius : 5 ) // light shading
Views and View Modifiers (custom modifiers, view extensions)
back to top
// ***** ALWAYS USE STRUCTS FOR YOUR VIEWS *****
// they are more efficient and hold far less data-taking values than classes.
// classes inherit all elements of data whether they use them or not.
// this causes classes to grow out of control as they gain all data from parents.
. frame ( maxWidth : 200 , maxHeight : 200 )
. background ( . green )
/* order of these modifiers matters; it will do the top item first, then
any that are below it. This means that in order for the background to fill
the 200x200 frame, the frame modifier must come first and then the
background color. If the background color was first, it would only highlight
the text of the button, and would not fill the 200x200 frame. */
. background ( . purple )
/* There is nothing behind our view, so if we want to have the background
of our entire screen be a certain color, we modify our outermost VStack
with maxWidth and maxHeight set to infinity. */
var spells : some View {
VStack {
Text ( " Bigby's Hand " )
Text ( " Fireball " )
}
}
/* Even stacks can be set up as variables! */
let character2 = Text ( " Frazil " )
// makes two constants containing a view as its property
character1
. foregroundStyle ( . red )
character2
. foregroundStyle ( . blue )
/* displays the text views stored in constants on screen.
Modifiers can be applied to these constants as well since they are
still views and can be used / manipulated as such */
spells
. foregroundStyle ( . green )
/* changes the text inside spells VStack to green */
Text ( " Paladin " )
Text ( " Necromancer " )
Text ( " Rogue " )
Text ( " Mage " )
. font ( . subheadline )
/* font applied to a view (like to this text view) takes priority over any
environment modifiers of the matching type. This is why Mage gets set as
smaller text than the other three text views above it in the same VStack */
Spacer ( )
} . font ( . title )
/* .font on the outside of the VStack is an environment modifier; as such
it applies to everything within the VStack .*/
struct CapsuleText : View { // create new struct for making capsule button stype
var text : String // expects string to be passed in at scturc callsite
var body : some View {
Text ( text )
. font ( . largeTitle )
. padding ( )
. foregroundStyle ( . white )
. background ( . blue )
. clipShape ( . capsule )
} // these are all the modifiers that will be applied to new CapsuleText struct iterations
}
struct ContentView : View { // start of screen displaying content
var body : some View {
VStack ( spacing : 10 ) { // new VStack
CapsuleText ( text : " First " )
/* makes a new CapsuleText instance, passing 'First' into struct.
Applies all modifiers automatically per struct instructions */
Text ( " Second " )
. font ( . largeTitle )
. padding ( )
. foregroundStyle ( . white )
. background ( . blue )
. clipShape ( . capsule )
} /* this creates the same button while having to enter all of the modifiers into the content view area
rather than calling it from the CapsuleText struct outside of it. */
struct Title : ViewModifier {
/* ex: we want to make the tiles in our app all look the same,
so rather than type out the code a bunch of times we make a struct
for the title formatting we wish to use repeatedly. This is called a view modifier */
func body ( content : Content ) -> some View {
// this line can be shortcut-entered by typing 'body' and selecting second option
content
. font ( . largeTitle )
. foregroundStyle ( . white )
. padding ( )
. background ( . blue )
. clipShape ( . rect ( cornerRadius : 10 ) )
} /* takes the Content from input and passes it into the func
which in this case applies modifiers to it */
}
struct Watermark : ViewModifier { // makes a new view modifier struct to iterate from
var text : String
func body ( content : Content ) -> some View { /* type body, select second option; used since views are involved
and we don't want to specify which type just yet */
ZStack ( alignment : . bottomTrailing ) { // places in the bottom right area of view
content
/* makes new ZStack (stackable view) with the content passed into the
struct at the callsite. */
Text ( text ) // passes parameter text from callsite
. font ( . caption )
. foregroundStyle ( . white )
. padding ( 5 )
. background ( . black )
// applies modifiers to parameter text
} // ZStack goes on top since it was last in line in function body
}
}
struct StatView : View { // new view factory: this is where views iterate from
let statName : String
let statScore : Int
// defines constants to be used in view body
var body : some View {
HStack {
Text ( " \( statName ) : " )
Text ( statScore , format : . number )
} // builds template for what each iteration of the view will look like
struct CardStyle : ViewModifier { // new view modifier factory
func body ( content : Content ) -> some View { // takes in content (in this case, a view)
content // places view here and applies all modifiers listed below
. frame ( maxWidth : . infinity , maxHeight : 100 )
. background ( . ultraThinMaterial )
. foregroundStyle ( . secondary )
. shadow ( radius : 5 )
. cornerRadius ( 25 )
back to top
back to top
ZStack {
//makes new ZStack (used for overlapping items)
Color . secondary
/* fills entire space with slightly transparent black to
give some shading abilities to other colors
** this Color.secondary could also be written as Color.colorName
(red, blue, green, purple, etc
- in the event we were to write the color in that way, we wound
remove the .background line of code from line 32
Can also make customer colors with the following syntax -
Color(red: 1, green: 0.8, blue: 0)
to tell swift to mix ratios of each of the RGB colors
} // ZStack goes on top since it was last in line in function body