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
-
Shapemust be able to create a path -
@NSManagedis 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
ForEachinsideListto 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
-
.userInterfaceIdiomtell 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
onChangeto observerPickerselected item changes and add action
Day 98
-
Spacer().frame(height: 0)create spacer with flexible width - apply
.font(.title)onImagecan show image larger
Day 97
-
static letislazy, 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 == .phonecheck current user’s device -
ListFormatterto 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
NavigationViewwith 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
NavigationLinkin first view, it will automatically load content in second view when iPhone is landscape - SwiftUI supports
either one or two childviews, even put more, they will be ignored
- UIKit
-
navigationBarHidden(true)to hide navigation bar -
alertandsheetwith optionals: use optionalIdentifiableobject 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
-
Groupas 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
-
GeometryReaderis given one value inside its layout closure, which is aGeometryProxycontaining 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
Doublerather 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:ContentViewsize 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-
ViewDimensionscontains 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
AlignmentIDto build custom type- provide
static defaultVal(in:)to acceptViewDimensionsand returnCGFloatspecify how view should be aligned if there is noalignmentGuide()modifier - set custom AlignmentID object to
enumrather 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. -
withAnimationto wrap remove function, the remaining will automatically slide up
Day 87
-
Timerto 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
upstreampublisher 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 -
willEnterForegroundNotificationwill notify when user re-active the app -
userDidTakeScreenshotNotificationwill notify user take screenshot -
significantTimeChangeNotificationwill notify when user changes the clock or when daylight savings time changes -
keyboardDidShowNotificationwill 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 -
onLongPressGestureto 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
-
UINotificationFeedbackGeneratorand.notificationOccurredto trigger built-in haptic- success, error, warning
-
CoreHapticsframework- make customize haptic, ex: combine taps, continuous vibrations, parameter curves, etc
- use
CHHapticEngine, the object responsible for creating vibrations-
.hapticIntensityto control how strong the haptic should be -
.hapticSharpnessto 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
-
@EnvironmentObjectproperty must conform toObservableObject -
@EnvironmentObjectonly 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
-
@EnvironmentObjectexplicitly 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
bodyproperty 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-
ObservableObjectwill 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-
.nonewill 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
pushview, environment will be shared - when
presentview, 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:
-
.environmentObjectto specify corresponding environment object
- when
-
TabViewshow 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
@Stateto 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
tagas tab identifier (recommend useStringrather than int identifier for this to help better understanding each tab’s work)
- use
- attach
Day 78
- use
CLLocationManagerto help fetching current user’s locationrequestWhenInUseAuthorizationstartUpdatingLocationlocationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
Day 77
- add
@propertyWrapperbefore 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
accessibilityAdjustableActionto adjust increment and decrement of Stepper- add
accessibilityElement(children: .ignore)andaccessibilityValueto 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-
combinewill 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
-
italictext was first used in Venice in 1500.
Day 72
- add
completeFileProtectionoptions 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
.italicto 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 = trueto show the call out- for showing it,
annotation must have a title
- for showing it,
-
annotationView.rightCallOutAccessoryViewcan customize call out view to show more information -
.detailDisclosureSwift 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-
calloutAccessoryControlTappedget called when button is tapped
-
Day 69
-
UIViewRepresentableas wrap of a UIView-
makeUIView()andupdateUIView()to handle instantiating and updating of a view when a SwiftUI state changes - UIViewRepresentable
Contextequal toUIViewRepresentable<Self>
-
-
MKMapViewto show the mapimport MapKit-
mapViewDidChangeVisibleRegiondelegate func check when map is zoom, rootate, moves -
MKPointAnnotationto display annotations -
mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation)to customize annotation mark -
MKPinAnnotationViewone of Apple annotation view design- set
canShowCallOut = truemeans tap the pin shows information then send it back
- set
- FaceID unlock
import LocalAuthentication-
LAContextto query biometric status and perform authentication check- use
canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometricsandevaluatePolicy()to evalute user’s biometric data
- use
- for simulator, use
Feature -> FaceID -> Enrolledto enable simulator faceID. useMatchingFaceorNon-matching faceto test fetch result
Day 68
-
FileManagerto 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 -
atomicwriting 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
-
@objcattribute 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
CIContextis good for performance
Day 66
- use
filter.inputKeys.containsto check if a filter key should be assigned for this filter
Day 65
-
context.createCGImage(output, from: output.extent):- use
output.extentto 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 -
CIVectoris 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
-
@propertyWrapperattribute 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.performBackgroundTaskto keep updating concurrently- Apple CoreData Concurrency Guide: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/Concurrency.html
Day 60
-
AnyViewvsGroup- 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 = .convertFromSnakeCasecan tell Swift to convert snake case to and from camel case.
Day 59
- fetch requests can be created by using
@FetchRequestorFetchRequeststruct
Day 58
- provide
NSPredicatein@FetchRequestto 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”
-
NSCompoundPredicateto build one predicate out of several smaller ones
- use
- Dynamically update FetchRequest
- okay to set
var fetchRequest: FetchRequest<T> - use
fetchRequest.wrappedValueproperty to get fetchedResult - For filter in any field
- use
%Kfor 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
NSSettoSet<>, use Swift native type
Day 57
-
\.selfto identify whole object- Swift compute hash value of the object
-
\.selfworks 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
-
CodegenselectManual/None Create NSManagedObject Subclass
-
-
@NSManagedread 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.hasChangesfirst, to avoid making CoreData do work that isn’t required
- before call it, always check
- CoreData constraints
-
Inspector -> Constraintsto change constraints - use
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicyto 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
-
constantbinding cannot be changed by user- its’s good for prototyping
Day 55
- use
NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)to create dummy test data in_Previewpart- this creat a managed object context involves telling system what concurrency type here want to use
-
mainQueueCurrencyTypeindicate to use main queue
- use
NSSortDescriptor(keyPath:, sorting:)to sort fetch request - use moc
.deleteto 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@Bindingproperty’s value in_Preview -
.onTapGesture {}to add tap action function
Day 53
-
@Bindingproperty wrapper to connect an@Stateproperty of one view to some underlying model data- create mutable value in the view
-
@Environment(\.horizontalSizeClass)tell screen sizes:compactorregular -
AnyViewtype erased wrapper- conform to
Viewprotocol 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
-
@Publishedproperty wrapper places properties inside aPublishedstruct behind the scenes. -
MIMEtypes 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 settingCodableobject 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 -> dismissandpush -> 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-
.normaldefault 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) -
animatableDataproperty: 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-oddrule to fill the shape -
ImagePainttype to wrap image that we can control how it should be rendered-
sourceRectfor 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-
hueis 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,addLineto start drawing some line - draw any where
- design to do specific thing
-
-
Shapeis View- draw inside rectangle (no rely on coordinates)
- built using path
- can draw space and accept parameters to customize further
-
StrokeStylecontrol 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 degreesas straight upward, instead it directlyto the right - Shape
measure the coordinates from bottom-left cornerrather than top-left corner (the clockwise not correct as our setting)
- SwiftUI not
-
strokeBordermodifier: make border visible- use
stokedirectly 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
insetAmountproperty 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
ShapefromInsettableShapecan make them able to callstrokeBordermodifier
- require
Day 42
-
NavigationLinkrequires aNavigationViewto work -
sheetNOT 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 -
layoutPriorityto control view shrinks/expands.- All views have layout priority of
0by default. Increase 1 means that view have higher priority to take available space.
- All views have layout priority of
Day 40
- DateFormatter,
mmmeanszero padded minute,MMmeanszero padded month -
JSONDecoder.dateDecodingStrategyto 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.widthto 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
Listonly 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
-
NavigationLinkto push the view in the view stack.- the destination view can be any view
-
List + NavigationLinkwill showgray indicator at the right sidein default
Day 38
-
UserDefaults integer(forKey:)will return0if 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_Previewpart if current View required some variables
Day 36
-
@Stateproperty wrapper- useful for simple local to current view data.
- with
structobject, everytime binding actually create newcopy, where SwiftUI notice its change, then update UI. - If change this to
classobject, 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)
-
@ObservedObjectproperty wrapper can help monitoring class variable changes- mark var as
@ObservedObjectto tell SwiftUI to watch class changes - extend class object as
ObservableObject, this is because@ObservedObjectcan only be used on types conform to it - set class property as
@Publishedproperty 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()-
@Environmentcreate properties which store values like: light or dark mode, smaller or larger fonts, timezone, etc. -
wrappedValueis required. becausepresentationModeis 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 -
IndexSettell 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.standarda built-in instance of UserDefaults
Day 35
-
ForEach- input range is
Range<Int>,NOT ClosedRange<Int>
- input range is
-
resizable()to makeImageView 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-
.scaleto scale up and down -
.asymmetricto add separate transition of show the view and disappear a view
-
-
UnitPointtype 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
-
.scaleEffectto apply scale effect -
.animation(.default)to apply default animation, which is “ease in, ease out” animation-
Animation.interpolatingSpringfor bouncing -
Animation.easeInOut.delayto 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
notset 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.
-
rotation3DEffectdefine 3 axis amount to determine how view rotate -
withAnimation()can define an animation
Day 31
- Place 2+ views in
List rowwill 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-
onAppearto set which function run when view is shown
-
Day 29
-
Listprovide scrolling table of database- use
listStyleto modify its view styles -
Listcan dynamically adding rows withoutForEach. ex:List(0..<5) { _ in ... } - use
id: \.selfto quick map each data, same asForEach
- use
- Use
UITextCheckerto 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-16is 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-16code units of a string’s utf16 view match the elements accessed through indexedNSStringAPIs
- The
Day 28
-
trailingshown on the right inleft-to-rightlanguages, it will be automatically flipped forright-to-left
Day 27
- Use
.navigationBarItems(trailing:)to add right bar button item at navigationBar - Use
static varvariable if we want to set one@Statevariable’s default value - Use
WheelDatePickerStyle()to present DatePicker as wheel style
Day 26
- Use of
Stepper: -+ button tapped to control increment and decrement - Use
DatePickerto bind date property-
displayComponentsto define which want to display -
Form + DatePickerclean format -
in:to define Date range
-
Day 25
-
VStack,Group,ForEachare 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,Spacerand more -
ForEach: loop over item by id ->ForEach(arr, id: \.self) { ... } - Use
Identifiableprotocol to identify views - Use
: Binding<>()to add custom bindings
Day 24
- SwiftUI use
structfor Views. use class might not compile
Day 23
- SwiftUI use
structfor 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:opaquereturn types- one specific type that conforms to
Viewprotocol - always return same type of View
- compiler know what view type is back, even we don’t known
-
VStackmakes 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:
fontisenvironmentmodifier which can be overridden,blurisregularmodifier 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
TextFiledbound to local property which will cause problem - okay to resemble view in SwiftUI. It’s also okay to define custom container.
- use
ViewModifierto 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.primarymight be light or dark depending on device theme -
Colorareviewsin SwiftUI
Day 21
-
Image.renderingMode(.original)render original image -
font,fontWeightmodifier of Text view -
clipShapemodifier 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,ZStackfor 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
Alertto add one alert. Also can useButton(){}.alert()to define when should we show alert. It will be run whenisPresentedcondition is true.
Day 19
- Swift have default Measurement instance to support unit conversion
- use
.symbolto get one Measurement Unit string
Day 18
- All SwiftUI views must inherit from
Viewprotocol. - views must contain at least one computed property which is
body. It can also contain more if we want. -
Forms can scroll - When
@Stateproperty changes, Swift re-invokebodyproperty. 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
NavigationViewoutside to make surePickerview can be pushed -
.navigationBarTitleNOT 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 -
.pickerStylecould help switch Picker style -
"$\(totalPerPerson, specifier: "%.2f")": usespecifier(C-style format string) to help control floating point.
Day 16
-
var body: some View:somerestrict the it must besamekind of view being returned. -
struct ContentView_Previews: PreviewProvider:PreviewProviderprotocol is for Xcode to show preview of UI design of the code - has limitation of
10children. 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 itprivateaccess 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.