This will record what I learned from 100 Days of SwiftUI. I will also use this to track my trial. Here is my practice repo: <https://github.com/HevaWu/100DaysOfSwiftUI
Day 100
-
Shape
must be able to create a path -
@NSManaged
is pre-dates property wrapper in Swift - Local modifier always override environment modifier from parents
- ex: if VStack has foreground color and some text inside also has foreground color. It will pick Text one.
- custom alignment guide must provide default value
- this will be used if we don’t attach
alignmentGuide()
to a view
- this will be used if we don’t attach
- use
ForEach
insideList
to mix dynamic rows alongside static rows - both
navigationViewStyle(StackNavigationViewStyle())
and.navigationViewStyle(.stack)
tell SwiftUI only show stack style (one view at a time) -
.labelsHidden()
to remove label from date picker- this is better than set empty label which might cause problems for screen readers
-
aspectRatio(contentMode: .fit)
=scaledToFit()
- Text fileds have no border by default
- it is okay to use
textFieldStyle()
to add border
- it is okay to use
- use
onDismiss()
to detect when a sheet is closed -
disabled()
can be used with methods, as long as it returns a Boolean - can attach
animation()
to a binding to trigger animation -
blur()
applies Gaussian blur
Day 99
-
.userInterfaceIdiom
tell whether device is tablet, phone or TV - All iOS devices have horizontal and vertical size classes
- Picker label is not shown well, this is known issue
- Use
onChange
to observerPicker
selected item changes and add action
Day 98
-
Spacer().frame(height: 0)
create spacer with flexible width - apply
.font(.title)
onImage
can show image larger
Day 97
-
static let
islazy
, which not got created until be used - On landscape iPhone, SwiftUI default behavior is show secondary view, with primary view as slide over.
-
UIDevice.current.userInterfaceIdiom == .phone
check current user’s device -
ListFormatter
to convert array of string into string- ex: [“A”, “B”, “C”] will return “A, B, and C”, more readable
Day 96
- 2 side by side view
- UIKit
UISplitViewController
- on iPad, show 2 views side by side
- on iPhone, collapse 2 views into one, get navigation view push-and-pop behavior
- SwiftUI
NavigationView
with two views as its body- a. on portrait, only see first view
- b. on large landscape iPHone, see second view, when swipe from left to right, show first view partially slide over second view
- c. on portrait iPads also see second view, when swipe from left to right, show first view partially slide over second view
- d. on landscape iPads see both first view and second view
- SwiftUI automatically link first and second view, if add
NavigationLink
in first view, it will automatically load content in second view when iPhone is landscape - SwiftUI supports
either one or two child
views, even put more, they will be ignored
- UIKit
-
navigationBarHidden(true)
to hide navigation bar -
alert
andsheet
with optionals: use optionalIdentifiable
object as condition, alert and sheet will be shown when object has value.- when alert dismissed, the object will
back to nil
- when alert dismissed, the object will
-
Group
as transparent layout container- can create series of views inside a group, then wrap group in different stack
-
@Environment(\.horizontalSizeClass)
to check current device horizontal layout(ex:.compact
)
Day 95
Day 94
-
GeometryReader
is given one value inside its layout closure, which is aGeometryProxy
containing layout information -
.background(GeometryReader { geo -> Color in })
is one way to get subview’s content geo value - When setting offset/degree on SwiftUI.Image, use
Double
rather than CGFloat to keep safe
Day 93
- position views
-
position()
, absolute position- once applied position(), get back new view
-
offset()
, not change original dimensions- only change location where view should be rendered without actually changing its underlying geometry
-
-
GeometryReader
- create size that was proposed by the parent, then use it to manipulate view
- view get returned has flexible preferred size, it will expand to take up more space as needed
-
frame(in:)
to read frame of a view -
coordinate spaces
-
global space
: measure view frame relative to whole screen -
local space
: measure view frame relative to parent
-
-
coordinateSpace()
to create custom coordinate spaces (any child of that can then read its frame relative to that coordinate space) - can grab values from view’s environment dynamically, then feed in its absolute or relative position ino various modifier
Day 92
-
layout neutral
:ContentView
size is exactly always size of its body -
ModifiedContent
, when apply a modifier to a view, SwiftUI actually get back new ModifiedContent, it stores both original giew and its modifier.- when apply a modifier, actual view goes into hierarchy is the modified view, not original one
- applying modifier creates new views rather than just modifying the existing views in-place
- ex:
Text().background
, text view becomes a child of its background
- if view hierarchy is wholly layout neutral, it will take up all available spaces (ex:
Color.red
) -
HStack(alignment: .lastTextBaseline)
: align text on baseline of last child -
alignmentGuide(Alignment, computeValue: (ViewDimensions))
, use to custom alignment-
ViewDimensions
contains width and height of the view, along with ability to read its various edges
-
-
offset()
will NOT change original dimensions - make custom
VerticalAlignment
/HorizontalAlignment
- use
AlignmentID
to build custom type- provide
static defaultVal(in:)
to acceptViewDimensions
and returnCGFloat
specify how view should be aligned if there is noalignmentGuide()
modifier - set custom AlignmentID object to
enum
rather than struct- by using
enum
, can’t make an instance there. Clearer this only house some functionality
- by using
- provide
- use
Day 91
- Only modern iPhone support Core Haptics
- Timer tolerance allows to delay timer for better energy efficiency.
- default is zero tolerance, but still doesn’t guarantee exact timings
Day 90
- Warm up Taptic Engine help reducing the latency between call
play()
and effect actually happening. It also have battery impact so the system will only stay ready for 1s or 2s after callprepare()
- OK call
prepare()
then never callplay()
. Sytem will keep Taptic Engine ready for a few seconds then power it down again. - if repeatedly call
prepare()
and never callplay()
, system might start ignoringprepare()
until at least oneplay()
happened - allow to call
prepare()
many ntimes before callingplay()
once.prepare()
doesn’t pause app while Taptic Engine warm up, not have any real performace cost when system is already prepared
- OK call
-
Image(decorative:)
make decorative image, that SwiftUI will ignores this image in Accessibility -
.navigationViewStyle(.stack)
tell SwiftUI only show stack style (one view at a time)- when rotate screen to landscape one, might face blank view issue. This is because SwiftUI allows 2 views sit side by side. Left side view decide show what, right side is details. Sometimes, might cause blank view there.
Day 89
Day 88
-
shadow(radius)
when add a view on top of another view which has same background color. To make current view clearly visible, can add shadow on current view to show it gentle depth effect. -
withAnimation
to wrap remove function, the remaining will automatically slide up
Day 87
-
Timer
to create Timer publisher- ex:
Timer.publish(every: 1, on: .main, in: .common).autoconnect()
- fire every 1s, run on main thread, run on common, connects timer immediately, assign whole thing to timer constant so that it stay alive
- use
upstream
publisher to find timer itself-
timer.upstream.connect().cancel()
will stop timer
-
- specify timer tolerance: ex:
Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
- ex:
-
onReceive()
to accept publisher, and run function - SwiftUI can notify NotificationCenter notifications
- ex:
onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification))
, will notify when user go to background -
willEnterForegroundNotification
will notify when user re-active the app -
userDidTakeScreenshotNotification
will notify user take screenshot -
significantTimeChangeNotification
will notify when user changes the clock or when daylight savings time changes -
keyboardDidShowNotification
will notify when keyboard is shown
- ex:
- Property to read user’s custom accessibility settings
-
@Environment(\.accessibilityDifferentiateWithoutColor)
can monitor user’s “Settings -> Accessibility -> Display & Text Size -> Differentiate Without Color” settings -
@Environment(\.accessibilityReduceMotion)
check user’s “Settings -> Accessibility -> Motion -> Reduce Motion” settings -
@Environment(\.accessibilityReduceTransparency)
check user’s “Settings -> Accessibility -> Display & Text Size -> Reduce Transparency” settings
-
Day 86
- Gesture
-
onTapGesture(count: 2)
to handle double taps -
onLongPressGesture
to handle long taps- call
onLongPressGesture(minimumDuration)
to specify duration -
pressing
: the change closure, will be called when user long press it
- call
-
gesture()
with specifying special gesture- ex: DragGesture, LongPressGesture, MagnificationGesture, RotationGesture, TapGesture
- have special modifier like
onEnded()
,onChanged()
- have special modifier like
- ex: DragGesture, LongPressGesture, MagnificationGesture, RotationGesture, TapGesture
- gesture clash: when 2 or more gestures that might be recognize at same time, SwiftUI always give child’s gesture high priority
- ex: one gesture attach to a view, and same gesture attached to its parent view
- if we want to change higher one’s priority, can use
highPriorityGesture()
to force set parent has high priority - use
simultaneousGesture()
to tell both parent and child to trigger at same time
- gesture sequence:
ges1.sequenced(before:ges2)
- force user do ges1 then ges2
-
- Haptic: small motors in device to create sensations such as taps and vibrations
-
UINotificationFeedbackGenerator
and.notificationOccurred
to trigger built-in haptic- success, error, warning
-
CoreHaptics
framework- make customize haptic, ex: combine taps, continuous vibrations, parameter curves, etc
- use
CHHapticEngine
, the object responsible for creating vibrations-
.hapticIntensity
to control how strong the haptic should be -
.hapticSharpness
to control how sharp the haptic it is- sharpness of 0, feel dull compare to value of 1
-
-
- Disable user interactivity
-
allowsHitTesting(flag:)
- when it is attached to a view with its parameter set to false, the view isn’t even consider tappable
-
contentShape()
, can specify tappable shape- useful when tap actions attached to stacks with spacers
-
Day 85
-
@EnvironmentObject
property must conform toObservableObject
-
@EnvironmentObject
only works withclass
Day 84
Day 83
-
.textContentType()
modifier to TextField to tell what kind of information this is asking for- system use this information to offer suggestions while user enter text on iOS or tvOS
-
CIFilter.qrCodeGenerator()
filter of QR code, to generate QR code image
Day 82
-
@EnvironmentObject
explicitly tell SwiftUI that object will exist in the environment by the time the view is created- if it isn’t present, app will crash immediately
- treat this as an implicitly unwrapped optional
- How SwiftUI know to refresh view when property changed?
- it doesn’t know it. When add @EnvironmentObject property, SwiftUI reinvoke
body
property whenever property changes.
- it doesn’t know it. When add @EnvironmentObject property, SwiftUI reinvoke
Day 81
- use
contextMenu()
to show attach context menu- when user press hard on it, it will show some views
- each item in context menu can have one text and one image attached
- always be
text first then image
(no matter order we type it)
Day 80
-
String(decoding: data, as: UTF8.self)
to convert Data to a string -
objectWillChange
, a publisher-
ObservableObject
will automatically gain this property - notifies view that are observing the object that something important has changed
- will be triggered immediately before make change
- call like
objectWillChange.send()
-
-
interpolation()
to control how pixel blending is applied-
.none
will turn off image interpolation entirely. (rather than blending, it will scale up small image with sharp edges)
-
Day 79
-
@EnvironmentObject
: place an object into the environment so that any child view can automatically have access to it- when
push
view, environment will be shared - when
present
view, not automatically shared data, Apple will change in the future - it will automatically look for corresponding instance, if it cannot find, it will crash
- ex:
@EnvironmentObject var user: User
, it will look for User
- ex:
-
.environmentObject
to specify corresponding environment object
- when
-
TabView
show tabBar- attach
.tabItem()
to each view inside TabView. For customize way the view shown in the tabBar - in
tabItem
, SwiftUI always show no more than one image and no more than one text view(even add more image and text view, it doesn’t matter) - programmatically control TabView current view(switch tab)
- use
@State
to track current selected tab, modify this property to new value to switch, pass this as binding and tell SwiftUI which tab should be shown -
TabView(selection:)
to bind with selected property - use
tag
as tab identifier (recommend useString
rather than int identifier for this to help better understanding each tab’s work)
- use
- attach
Day 78
- use
CLLocationManager
to help fetching current user’s locationrequestWhenInUseAuthorization
startUpdatingLocation
locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
Day 77
- add
@propertyWrapper
before a struct, to define custom property wrapper -
.jpegData()
to save image as jpeg format-
compressionQuality
: range from 0(low quality) to 1(high quality). Normally pick 0.8
-
Day 76
- use
accessibilityAdjustableAction
to adjust increment and decrement of Stepper- add
accessibilityElement(children: .ignore)
andaccessibilityValue
to read Stepper value
- add
Day 75
Day 74
- use
.accessibility(label:)
and.accessibility(hint:)
to control what VoiceOver reads - use
.accessibilityAddTraits(.isButton)
to provide extra behind scenes information to VoiceOver to describes how view works- similarly, have
accessibilityRemoveTraits()
- similarly, have
-
Image(decorative:)
tell SwiftUI it should be ignored by SwiftUI- it will not read out the image’s filename as the automatic VoiceOver label. If we add label or a hint that will be read
- can use
.accessibilityHidden()
to make view completely invisible to the accessibility system
-
accessibilityElement(children: .combine)
apply this to parent view can combine its children into a single accessibility element-
combine
will have a pause between two pieces of text - use
accessibilityElement(children: .ignore)
+accessibilityLabel(Text)
is more natural way, text will read all at once
-
Day 73
-
italic
text was first used in Venice in 1500.
Day 72
- add
completeFileProtection
options when write data to fileManager, to make file accessable only while device is unlocked
Day 71
-
+
to add text views together- this can create larger text views that mix and match different kind of formatting
- use
.italic
to show text with italic
Day 70
- to add pin on the MapView in SwiftUI
- use Binding to bind centerCoordinator
- add annotations property to record current map annotations
-
mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
-
dequeueReusableAnnotationView(withIdentifier:)
to try reuse view -
annotationView.canShowCallout = true
to show the call out- for showing it,
annotation must have a title
- for showing it,
-
annotationView.rightCallOutAccessoryView
can customize call out view to show more information -
.detailDisclosure
Swift Button style, show button like an “i” with a circle around it
-
-
mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
to update call out view for one annotation-
calloutAccessoryControlTapped
get called when button is tapped
-
Day 69
-
UIViewRepresentable
as wrap of a UIView-
makeUIView()
andupdateUIView()
to handle instantiating and updating of a view when a SwiftUI state changes - UIViewRepresentable
Context
equal toUIViewRepresentable<Self>
-
-
MKMapView
to show the mapimport MapKit
-
mapViewDidChangeVisibleRegion
delegate func check when map is zoom, rootate, moves -
MKPointAnnotation
to display annotations -
mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation)
to customize annotation mark -
MKPinAnnotationView
one of Apple annotation view design- set
canShowCallOut = true
means tap the pin shows information then send it back
- set
- FaceID unlock
import LocalAuthentication
-
LAContext
to query biometric status and perform authentication check- use
canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics
andevaluatePolicy()
to evalute user’s biometric data
- use
- for simulator, use
Feature -> FaceID -> Enrolled
to enable simulator faceID. useMatchingFace
orNon-matching face
to test fetch result
Day 68
-
FileManager
to find document direcrtory of current user- when app deleted, this directory will automatically deleted
- no physical limitation, but user can check it at Settings app
- ex:
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
- use
write(to:)
to write data- use
String(contentsOf:)
andData(contentsOf:)
to read data -
atomic
writing data- system write full file to temporary filename, and when its finished it does a rename to target filename
- either whole file is there, or nothing is
- this is safer, otherwise, if we set atomic as false, it might cause problem that: when reading data, writing might not finish yet, so only read part of data
- use
Day 67
-
@objc
attribute lets Objective-C code call a Swift method - can place optional views directly into SwiftUI View hierarchy
- SwiftUI will only render them if they have a value
- reuse
CIContext
is good for performance
Day 66
- use
filter.inputKeys.contains
to check if a filter key should be assigned for this filter
Day 65
-
context.createCGImage(output, from: output.extent)
:- use
output.extent
to get proper rect property
- use
- for filter, use
setValue()
would be safer - use
Binding<Double>
to bind filter param with user interface
Day 64
- SwiftUI Coordinator
- design to act as delegate for UIKit view controllers
-
class Coordinator
, has to be class - implement
makeCoordinator()
to create and configure Coordinator instance -
xx.delegate = context.coordinator
, don’t call makeCoordinator(), SwiftUI will automatically call and associate coordinator. When makeUIViewController() and updateUIViewController() calls, automatically pass the coordinator object -
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate
:-
NSObject
: allows objective-c to ask object what functionality it supports at runtime -
UIImagePickerControllerDelegate
: NSObject let objective-c check for the functionality, this protocol is waht actually provide it -
UINavigationControllerDelegate
: detect when user move between screens
-
- add property and environment in struct, then in Coordinator class, update the property inside delegate function
- in where we call the view, use property to update the view UI components
- save image to photo
- UIKit use
UIImageWriteToSavedPhotoAlbum()
- SwiftUI can use a class to wrapper and implement #selector function to it
- UIKit use
Day 63
- image types
-
UIImage
, form UIKit, capable of variety of image types, including bitmaps(like PNG), vectors(like SVG), even sequences form an animation -
CGImagae
, from Core Graphics, 2D array of pixels -
CIImage
, from Core Image, store all information required to produce an image but doesn’t actually turn that into pixels unless it’s asked to. an “image recipe”
-
- CoreImage filter:
import CoreImage.CIFilterBuiltins
- sepial filter:
CIFilter.sepiaTone()
, intensity is between 0(original image) to 1(full sepia) - pixellation filter:
CIFilter.pixellate()
, sclae = 100 means pixels are 100 points across - crystal effect:
CIFilter.crystallize
- also can set input image for a filter by using
kCIInputImageKey
-
CIVector
is CoreImage’s way of storing points and directions - Read output image in SwiftUI: CIImage -> CGImage -> UIImage -> Image
- sepial filter:
- wrap UIKit view controller
- extend a struct from
UIViewControllerRepresentable
, which build onView
- implement
makeUIViewController()
andupdateUIViewController()
- extend a struct from
Day 62
- @State
-
@propertyWrapper
attribute allow make State of struct - have a
wrappedValue
, which is actual value SwiftUI trying to store
-
-
Binding<Value>
to make new binding object- can be used to observing @State element changes
- init need
get: {}, set: {}
- both get and set are
@escaping
, Binding struct stores them for use later on
- use
actionsheet()
to show ActionSheet- style of buttons:
default()
,cancel()
,destructive()
- usage similar to alert
- style of buttons:
Day 61
- use
PersistenceController.shared.container.performBackgroundTask
to keep updating concurrently- Apple CoreData Concurrency Guide: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/Concurrency.html
Day 60
-
AnyView
vsGroup
- both can contains different type of views inside its closure
- array type
-
[Group]
, SwiftUI can’t make this type, because it is no meaning, SwiftUI want to know what’s in the group -
[AnyView]
is okay, because AnyView is the contents
-
- define
decoder.keyDecodingStrategy = .convertFromSnakeCase
can tell Swift to convert snake case to and from camel case.
Day 59
- fetch requests can be created by using
@FetchRequest
orFetchRequest
struct
Day 58
- provide
NSPredicate
in@FetchRequest
to control which results should be shown- use
%@
means “insert some data here”- “IN”, “BEGINSWITH”(case sensitive), “BEGINSWITH[c]”(ignore the case), “CONTAINS[c]”, “NOT”, “AND”
-
NSCompoundPredicate
to build one predicate out of several smaller ones
- use
- Dynamically update FetchRequest
- okay to set
var fetchRequest: FetchRequest<T>
- use
fetchRequest.wrappedValue
property to get fetchedResult - For filter in any field
- use
%K
for specify filter key, insert value but not add extra quote mark around - use
%@
will add extra quote mark - ex:
-
"%@ BEGINSWITH %@", lastName, S
=="'lastName' BEGINSWITH 'S'"
-
"%K BEGINSWITH %@", lastName, S
=="lastName' BEGINSWITH 'S'"
-
- use
- okay to set
- CoreData Relationships
- 4 forms: 1 to 1, 1 to many, many to 1, many to many
- convert
NSSet
toSet<>
, use Swift native type
Day 57
-
\.self
to identify whole object- Swift compute hash value of the object
-
\.self
works for anything conform toHashable
- Core Data generate object ID sequentially we create objects. These ID are unique to the object.
-
Hashable
: though calculating same hash for an object twice should return same value. If calculating it between 2 runs of the app (quit then relaunch), the hash can return different values. - manual manage object context
-
Codegen
selectManual/None
Create NSManagedObject Subclass
-
-
@NSManaged
read and write from a directory that CoreData uses to store the information- benefits: when read data, transparently fetches the data and sends it back
- CoreData is
lazy
- sometimes looks like data is loaded when it really hasn’t been
- these are faults, sense of fault line - a line between where something exists and where something is just a placeholder
-
NSManagedObjectContext.save()
- before call it, always check
moc.hasChanges
first, to avoid making CoreData do work that isn’t required
- before call it, always check
- CoreData constraints
-
Inspector -> Constraints
to change constraints - use
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
to tell CoreData always keep constraint attribute unique - when there is duplicates object, only one data get written
-
Day 56
- SFSymbbols automatically adapts to the text around it.
- it can be shown larger or small by using
font()
modifier
- it can be shown larger or small by using
-
constant
binding cannot be changed by user- its’s good for prototyping
Day 55
- use
NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
to create dummy test data in_Preview
part- this creat a managed object context involves telling system what concurrency type here want to use
-
mainQueueCurrencyType
indicate to use main queue
- use
NSSortDescriptor(keyPath:, sorting:)
to sort fetch request - use moc
.delete
to delete one data record from fetch request
Day 54
- use
.environment(\.managedObjectContext, moc)
to pass environment setting to another view- use it to write values in the environment
- when use sheet() to present the view, need to add a managed object context property to pass it
- when use push, current view will share environment setting with its ancestor
- use
.constant()
to set default@Binding
property’s value in_Preview
-
.onTapGesture {}
to add tap action function
Day 53
-
@Binding
property wrapper to connect an@State
property of one view to some underlying model data- create mutable value in the view
-
@Environment(\.horizontalSizeClass)
tell screen sizes:compact
orregular
-
AnyView
type erased wrapper- conform to
View
protocol as Text, Color, VStack, etc. - doesn’t expose what it contains (Swift see the returning only AnyView, which considered the same type)
- DON’T use it anytime, only use when requires
- if SwiftUI knows exactly view types, it can add and remove small parts trivially as needed
- if use AnyView, the above will be denied
- conform to
-
@FetchRequest(entity:sortDescriptors)
property wrapper to get core data fetch request, with type ofFetchedResults<>
-
@Environment(\.managedObjectContext)
property wrapper to get current managed object context
Day 52
-
@Published
property wrapper places properties inside aPublished
struct behind the scenes. -
MIME
types were invented fro email attachments, and is short for Multipurpose INternet Mail Extensions
Day 51
Day 50
- add
.animation()
after one published property to see animation when this property changes
Day 49
- add
required init(from decoder:)
andencode(to encoder:)
function to allow settingCodable
object property as@Published
- need to implement Codable Conformance itself
- use
disabled()
to control under which condition disable the element
Day 47
-
presentationMode.wrappedValue.dismiss()
work for bothpresent -> dismiss
andpush -> pop
-
_count = State(initialValue: activity.count)
to set initial value ofState
Day 46
-
saturation() / blur()
: able to be applied by any view in real-time -
stroke()
draw a line around a path that is half-way inside the line and half-way outside -
stokeBorder()
draw stroke to be entirely within the shape -
AnimatablePair<>
only animate values that are animatable, which excludes integers.
Day 45
-
blendMode()
to specify blend mode-
.normal
default value -
.multiply
: multiply each source pixel color with destination pixel color. Like a tint effect -
.screen
: inverts the colors then perform multiply, then inverts them again, resulting with a brighter image
-
-
colorMultiply()
directly blend Color modes -
saturation()
adjust how much color used in side a view. value is in range 0(no color, gray scale) to 1(full color) -
animatableData
property: use to animate one shape, to help see smooth animation changing. Always be one value. -
AnimatablePair<,>
: contains pair of animatable values, animate more variable- ex: SwiftUI’s EdgeInsets
AnimatablePair<CGFloat, AnimatablePair<CGFloat, AnimatablePair<CGFloat, CGFloat>>>
- ex: SwiftUI’s EdgeInsets
Day 44
-
.fill(, style: FillStyle(eoFill: true))
to applyeven-odd
rule to fill the shape -
ImagePaint
type to wrap image that we can control how it should be rendered-
sourceRect
for specify rect within image, range of 0(start) to 1(end) - useful add for view backgrounds and shape strokes
-
- any
bellow 60fps render has problem(slow)
, many iOS render at 120fps -
Color(hue:saturation:brightness:)
make color from hue-
hue
is value from 0 to 1 controlling kind of color we see. res is both 0 and 1, other hues in between
-
-
drawingGroup()
modifier tell SwiftUI should render the contents of the view into an off-screen image before putting it back onto the screen as a single rendered output.- powered by Metal, working directly with GPU
- NOTE: add this might slow down SwiftUI for simple drawing. Add it when there is really a performance problem there.
Day 43
-
Path
: SwiftUI use this to draw custom shapes-
moveTo
,addLine
to start drawing some line - draw any where
- design to do specific thing
-
-
Shape
is View- draw inside rectangle (no rely on coordinates)
- built using path
- can draw space and accept parameters to customize further
-
StrokeStyle
control how line should be connected to line after it (lineJoin
), and how line should be drawn when it ends without a connection after it (lineCap
) - Trickle in drawing Arc:
- SwiftUI not
treat 0 degrees
as straight upward, instead it directlyto the right
- Shape
measure the coordinates from bottom-left corner
rather than top-left corner (the clockwise not correct as our setting)
- SwiftUI not
-
strokeBorder
modifier: make border visible- use
stoke
directly might get border out of edge of the screen (which means the outside part of the border ends up beyond screen edges)
- use
-
InsettableShape
: a shape can be inset by a certain amount to produce another shape.- require
inset(by:)
, this function given the inset amount(half the line width of stoke), and return a new kind of insettable shape.- since don’t know the actual size of the shape, it’s okay to hold a
insetAmount
property to record the inset amount there. Then call it where required to be set
- since don’t know the actual size of the shape, it’s okay to hold a
- extend custom
Shape
fromInsettableShape
can make them able to callstrokeBorder
modifier
- require
Day 42
-
NavigationLink
requires aNavigationView
to work -
sheet
NOT requireNavigationView
Day 41
-
Spacer(minLength:)
: to define minimum size of Space.- helpful in scroll view since total height is flexible
- helpful define space min length in HStack and VStack
-
NavigationLink.buttonStyle()
: use to change linked button style -
layoutPriority
to control view shrinks/expands.- All views have layout priority of
0
by default. Increase 1 means that view have higher priority to take available space.
- All views have layout priority of
Day 40
- DateFormatter,
mm
meanszero padded minute
,MM
meanszero padded month
-
JSONDecoder.dateDecodingStrategy
to tell how it should decode dates. It need to be careful at timeZone part.
Day 39
-
aspectRatio()
could set contentMode (fit or fill) -
GeometryReader
: a view can be used to handleGeometryProxy
, which able to query environment, ex: big of container, position of view, safe area insets, etc- use like
GeometryReader { geo in ... }
- use
geo.size.width
to fill the width of screen
- use like
-
ScrollView
: use to make UIScrollView like view- when add the view to scroll view, it get create immediately, ex: for 100 items, scrollView will generate all 100 items at the first. While
List
only create items when it is needed
- when add the view to scroll view, it get create immediately, ex: for 100 items, scrollView will generate all 100 items at the first. While
-
NavigationLink
to push the view in the view stack.- the destination view can be any view
-
List + NavigationLink
will showgray indicator at the right side
in default
Day 38
-
UserDefaults integer(forKey:)
will return0
if key doesn’t exist - JSON stand for JavaScript Object Notion
Day 37
- Set object as
Identifiable
, then inForEach
, it will be okay to removeid:
things, ex:ForEach(items) {...}
-
SwiftUI View
: it is okay to put dummy object in_Preview
part if current View required some variables
Day 36
-
@State
property wrapper- useful for simple local to current view data.
- with
struct
object, everytime binding actually create newcopy
, where SwiftUI notice its change, then update UI. - If change this to
class
object, it might not show properly(because with class, SwiftUI can modify its value directly, where the actual property does not change in fact, which means State didn’t monitor it). So the values in class does change, but the view isn’t being reloaded to reflect the change - when we want to share data between views, better to use other property wrappers(ex:
@ObservedObject
,@EnvironmentObject
)
-
@ObservedObject
property wrapper can help monitoring class variable changes- mark var as
@ObservedObject
to tell SwiftUI to watch class changes - extend class object as
ObservableObject
, this is because@ObservedObject
can only be used on types conform to it - set class property as
@Published
property observer
- mark var as
-
sheet(isPresented:)
: present another view on top of current one- default is card presentation style
- swipe down can dismiss it
- dismiss by trigger button: use
@Environment(\.presentationMode)
to attached presentation mode variable stored in app’s environment. Then dismiss by callingpresentationMode.wrappedValue.dismiss()
-
@Environment
create properties which store values like: light or dark mode, smaller or larger fonts, timezone, etc. -
wrappedValue
is required. becausepresentationMode
is actually a binding that can be updated automatically by system
-
-
onDelete(performed: { indexSet in })
inList { ForEach }
to provide delete row function-
onDelete()
only exist inForEach
-
IndexSet
tell position of all items in ForEach that should be removed. It’s like a set of integers, which its sorted
-
-
.navigationBarItems(leading: EditButton())
show Edit/Down button to navigation bar -
UserDefaults
: store small amount of user data(better be no more than 512KB)- the data stored there will automatically be loaded when app launches.
- store user settings and important data
-
UserDefaults.standard
a built-in instance of UserDefaults
Day 35
-
ForEach
- input range is
Range<Int>
,NOT ClosedRange<Int>
- input range is
-
resizable()
to makeImage
View fit its space
Day 34
-
offset()
modifier used to move a view relative to its natural position
Day 33
- can attach
multiple animation()
modifiers but the order matters - can disable animation by setting
.animation(nil)
- Gesture have
onChanged()
andonEnded()
action block -
transition()
modifier could help apply transitions to view-
.scale
to scale up and down -
.asymmetric
to add separate transition of show the view and disappear a view
-
-
UnitPoint
type for controlling anchor, to specify exact X/Y for rotation or use builtin options:.topLeading
,.bottomTrailing
, etc -
extension AnyTransition { static var }
to add custom transition
Day 32
-
.scaleEffect
to apply scale effect -
.animation(.default)
to apply default animation, which is “ease in, ease out” animation-
Animation.interpolatingSpring
for bouncing -
Animation.easeInOut.delay
to apply delay time on animation - can also set
repeatCount(,autoreverses)
,repeatForever(autoreverses)
to repeat animation - repeat setting will trigger
onAppear()
-
- can apply
animation()
forbinder
, this will add bind animations.- This will do implicitly animations.
- this one
not
set animation on View and animate it with state change. (state change don’t know it trigger animation) - this one set nothing on view and explicitly with a state change. (view don’t know it will be animated)
- this one
- This will do implicitly animations.
-
rotation3DEffect
define 3 axis amount to determine how view rotate -
withAnimation()
can define an animation
Day 31
- Place 2+ views in
List row
will create implicitHStack
Day 30
-
TextFiled
-
textFiledStyle()
to update style -
onCommit:
to set function when user press return on the keyboard -
autocapitalization()
to set capitalization
-
-
NavigationView
-
onAppear
to set which function run when view is shown
-
Day 29
-
List
provide scrolling table of database- use
listStyle
to modify its view styles -
List
can dynamically adding rows withoutForEach
. ex:List(0..<5) { _ in ... }
- use
id: \.self
to quick map each data, same asForEach
- use
- Use
UITextChecker
to help checking misspelled word- call
rangeOfMisspelledWord(in:range:startubgAt:wrap:language:)
to check it find any. It send back another Objective-C string range. If there is some, return range. If not, returnNSNotFound
- call
-
UTF-16
is character encoding, it is useful to let objective-c to understand how Swift’s string stored, which is nice binding format to connect the two- The
UTF-16
code units of a string’s utf16 view match the elements accessed through indexedNSString
APIs
- The
Day 28
-
trailing
shown on the right inleft-to-right
languages, it will be automatically flipped forright-to-left
Day 27
- Use
.navigationBarItems(trailing:)
to add right bar button item at navigationBar - Use
static var
variable if we want to set one@State
variable’s default value - Use
WheelDatePickerStyle()
to present DatePicker as wheel style
Day 26
- Use of
Stepper
: -+ button tapped to control increment and decrement - Use
DatePicker
to bind date property-
displayComponents
to define which want to display -
Form + DatePicker
clean format -
in:
to define Date range
-
Day 25
-
VStack
,Group
,ForEach
are all views in SwiftUI -
primitive views
, building blocks of SwiftUI, which conform to View but return some fixed content rather than rendering some other kind of view instead. ex:Text
,Image
,Color
,Spacer
and more -
ForEach
: loop over item by id ->ForEach(arr, id: \.self) { ... }
- Use
Identifiable
protocol to identify views - Use
: Binding<>()
to add custom bindings
Day 24
- SwiftUI use
struct
for Views. use class might not compile
Day 23
- SwiftUI use
struct
for Views. functional design. -
UIHostingController
: bridge between UIKit and SwiftUI. - modifier order is important.
-
type(of:)
prints exact type of particular value. -
ModifiedContent
: once we apply one modifier, stack upModifiedContent<ModifiedContent<...>>
-
- use
some View
:opaque
return types- one specific type that conforms to
View
protocol - always return same type of View
- compiler know what view type is back, even we don’t known
-
VStack
makes SwiftUI createsTupleView<T>
which wrote to handle at most 10 types views. That’s why SwiftUI cannot handle more than 10 views
- one specific type that conforms to
-
ternary operator
: can be used as condition checking -
environment modifier
: can apply to all container views- ex:
font
isenvironment
modifier which can be overridden,blur
isregular
modifier which cannot replace child view’s setting
- ex:
- okay to create view as property. But cannot create one property refers to other stored properties, ex: a
TextFiled
bound to local property which will cause problem - okay to resemble view in SwiftUI. It’s also okay to define custom container.
- use
ViewModifier
to define custom modifier. And call it by.modifier
Day 22
- Decorative images are not read by the screen reader.
- Semantic colors are named according to the use. ex:
Color.primary
might be light or dark depending on device theme -
Color
areviews
in SwiftUI
Day 21
-
Image.renderingMode(.original)
render original image -
font
,fontWeight
modifier of Text view -
clipShape
modifier to use built in shape, ex: rectangle, rounded rectangle, circle, capsule -
overlay()
modifier to drawing above previous -
shadow()
modifier to apply shadow effectDay 20
-
HStack
,VStack
,ZStack
for horizontal, vertical, zepth stack. Has limitation of maximum of 10 children. If we want to add more, useGroup
-
Color
, can useframe()
modifier to change specific sizes - Use
edgesIgnoringSafeArea(.all)
modifier ignore the safeArea insets -
LinearGradient
,RadialGradient
,AngularGradient(Conic gradient)
-
Button(action:label:)
can help add customize Button design -
Image
-
Image("pencil")
: load image already added in project -
Image(decorative: "pencil")
: load image added in project, but not read it out for screen reader user -
Image(systemName: "pencil")
: load image embedded in iOS, use Apple’s SF Symbols
-
- Use
Alert
to add one alert. Also can useButton(){}.alert()
to define when should we show alert. It will be run whenisPresented
condition is true.
Day 19
- Swift have default Measurement instance to support unit conversion
- use
.symbol
to get one Measurement Unit string
Day 18
- All SwiftUI views must inherit from
View
protocol. - views must contain at least one computed property which is
body
. It can also contain more if we want. -
Form
s can scroll - When
@State
property changes, Swift re-invokebody
property. This forces all values inside the body to be re-evaluated, making sure they are updated for the changes.
Day 17
-
.keyboardType()
: can specify type of keyboard, but user still can input other values by copy/paste. - Add
NavigationView
outside to make surePicker
view can be pushed -
.navigationBarTitle
NOT add directly end of NavigationView, because navigation views are capable of showing many views when program runs. Attach title to the object inside NavigationView can make code change title freely. -
Section(header:)
could help define Section header, same for footer -
.pickerStyle
could help switch Picker style -
"$\(totalPerPerson, specifier: "%.2f")"
: usespecifier
(C-style format string) to help control floating point.
Day 16
-
var body: some View
:some
restrict the it must besame
kind of view being returned. -
struct ContentView_Previews: PreviewProvider
:PreviewProvider
protocol is for Xcode to show preview of UI design of the code - has limitation of
10
children. For more, useGroup
-
NavigationView
: add navigation bar to avoid “scrolling content view to the top which overlapped with statusBar” -
@State
: a property wrapper which allows value store separately by SwiftUI in a place thatcan be modified
. It is designed for simple properties stored in one view. Apple recommends give itprivate
access control -
$name
: two way binding(bind text field to show value of property, also bind any changes in text field to update property)
Comments
Join the discussion for this article at here . Our comments is using Github Issues. All of posted comments will display at this page instantly.