6

Fucking SwiftUI - Cheat Sheet

 2 years ago
source link: https://fuckingswiftui.com/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Fucking SwiftUI

Fucking SwiftUI is a curated list of questions and answers about SwiftUI.

You can track change in Changelog

All the answers you found here don't mean to be complete or detail, the purpose here is to act as a cheat sheet or a place that you can pick up keywords you can use to search for more detail.

Unable to access this site due to the profanity in the URL? https://goshdarnswiftui.com is a more work-friendly mirror.

Frequently asked questions about SwiftUI.

Should I learn SwiftUI?

Should I learn it now?

It depends since SwiftUI runs on iOS 13, macOS 10.15, tvOS 13, and watchOS 6. If you work on a new app that plans to target only the mentioned OS, I would say yes. If you plan to find a job or work on a client project in which you have no control over this OS version, you might want to wait a year or two before considering moving to SwiftUI. Because most client work would like to support as much as users as possible, that means you have to work on an app that supports iOS N-1, N-2, or worse N-3. So the best case would be a year until you can get a hand on this lovely SwiftUI.

iOS 14

A new WidgetKit framework in iOS 14 is exclusive to SwiftUI, so you might need to learn it this year if you want to support a new widget.

Should I learn UIKit?

Yes, UIKit would still be an important part of the iOS world for quite some time. At the current stage, SwiftUI is still missing many features, and I think even you start fresh with SwiftUI, you still need to come back to UIKit from time to time.

iOS 14

iOS 14 closing some gap, but the point remains. You need to come back to UIKit when you hit a roadblock.

Does SwiftUI replace UIKit?

Not right now, but I can see it might in the future. Apple just introduces SwiftUI, and it already looks great. I expect both to coexist for a long time, SwiftUI is very young and needs years to grow to be able to replace its ancestor.

If I can learn one thing today, what would it be UIKit or SwiftUI?

Too bad I don't have the answer to this question. Both UIKit and SwiftUI are different beasts with different ways of thinking. I suggest you try both of them and judge for yourself which paradigm you like the most. No matter what technologies you choose, one thing I can assure you is you can create a great app out of either one of them.

Where is a view controller in SwiftUI?

They are gone. Now views talk with others via the new reactive framework, Combine. This new approach work as a replacement for UIViewController, which is just a way of communication.

Minimum Requirements

  • Xcode 11 for SwiftUI and Xcode 12 beta for iOS 14 features (Download beta software from Apple)
  • iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6
  • macOS Catalina in order to have SwiftUI render in the canvas.

Want to try beta SwiftUI features, but don't want to install a new beta OS on your machine

You can install beta OS in parallel to your current macOS version. Instruction here Installing macOS on a separate APFS volume

UIKit equivalent in SwiftUI

View Controllers

UIKit SwiftUI Note UIViewController View

UITableViewController List You can also use ScrollView with LazyHStack or LazyVStack UICollectionViewController LazyVGrid and LazyHGrid Currently there is no SwiftUI view replacement for this, but you can simulate some layout with composing of List as in Composing Complex Interfaces's tutorial, In iOS 14, we now have LazyVGrid and LazyHGrid. UISplitViewController NavigationView

UINavigationController NavigationView

UIPageViewController TabView A style of TabView in iOS 14 UITabBarController TabView

UISearchController -

UIImagePickerController -

UIVideoEditorController -

UIActivityViewController -

UIAlertController Alert

Views and Controls

UIKit SwiftUI Note UILabel Text, Label

UITabBar TabView

UITabBarItem TabView .tabItem under TabView UITextField TextField For password (isSecureTextEntry) use SecureField UITextView TextEditor iOS 14 UITableView List also VStack and Form UINavigationBar NavigationView Part of NavigationView UINavigationItem ToolbarItem iOS 14 UIBarButtonItem NavigationView .navigationBarItems in NavigationView UICollectionView LazyVGrid and LazyHGrid iOS 14 UIStackView HStack, LazyHStack .axis == .Horizontal UIStackView VStack, LazyVStack .axis == .Vertical UIScrollView ScrollView

UIActivityIndicatorView ProgressView with CircularProgressViewStyle iOS 14 UIImageView Image

UIPickerView Picker

UIButton Button, Link

UIDatePicker DatePicker

UIPageControl

iOS 14. Auto add to TabView with PageTabViewStyle style. You can control its appearance by .indexViewStyle. UIProgressView ProgressView iOS 14 UISegmentedControl Picker A style (SegmentedPickerStyle) of Picker UISlider Slider

UIStepper Stepper

UISwitch Toggle

UIToolBar NavigationView with .toolbar iOS 14 MKMapView Map import MKMapView to use this view

Framework Integration - UIKit in SwiftUI

Integrate SwiftUI views into existing apps, and embed UIKit views and controllers into SwiftUI view hierarchies.

Framework Integration - SwiftUI in UIKit

Integrate SwiftUI views into existing apps, and embed UIKit views and controllers into SwiftUI view hierarchies.

UIKit SwiftUI Note UIView (UIHostingController) View There is no direct convert to UIView, but you can use container view to add view from UIViewController into view hierarchy UIViewController (UIHostingController) View

Pure SwiftUI

iOS 14

In iOS 14, you can write the whole app without a need for UIKit. Checkout App essentials in SwiftUI session from WWDC2020.

UIKit SwiftUI Note UIApplicationDelegate App

UIWindowSceneDelegate Scene

SwiftUI - Views and Controls

A view that displays one or more lines of read-only text.

Text("Hello World")

Styling

Text("Hello World")
.bold()
.italic()
.underline()
.lineLimit(2)

String provided in Text also used as LocalizedStringKey, so you get NSLocalizedString's behavior for free.

Text("This text used as localized key")

To format text inside text view. Actually this is not SwiftUI feature, but Swift 5 String interpolation.

static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}()

var now = Date()
var body: some View {
Text("What time is it?: \(now, formatter: Self.dateFormatter)")
}

You can also concatenate Text together with +.

Text("Hello ") + Text("World!").bold()

Text alignment.

Text("Hello\nWorld!").multilineTextAlignment(.center)

Documentation - Text

Label

iOS 14

Label is a convenient view that presents an image and text alongside each other. This is suitable for a menu item or your settings.

You can use your own image or SF Symbol.

Label("Swift", image: "swift")
Label("Website", systemImage: "globe")

I can't find a way to adjust image size, and SF Symbols is misaligned with text in the first beta.

Documentation - Label

TextField

A control that displays an editable text interface.

@State var name: String = "John"    
var body: some View {
TextField("Name's placeholder", text: $name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}

Documentation - TextField

TextEditor

iOS 14

A view that can display and edit long-form text.

@State private var fullText: String = "This is some editable text..."

var body: some View {
TextEditor(text: $fullText)
}

Documentation - TextEditor

SecureField

A control into which the user securely enters private text.

@State var password: String = "1234"    
var body: some View {
SecureField($password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}

Documentation - SecureField

Image

A view that displays an environment-dependent image.

Image("foo") //image name is foo

We can use new SF Symbols

Image(systemName: "clock.fill")

you can add style to system icon set to match font you use

Image(systemName: "cloud.heavyrain.fill")
.foregroundColor(.red)
.font(.title)
Image(systemName: "clock")
.foregroundColor(.red)
.font(Font.system(.largeTitle).bold())

Add style to Image

Image("foo")
.resizable() // it will sized so that it fills all the available space
.aspectRatio(contentMode: .fit)

Documentation - Image

Button

A control that performs an action when triggered.

Button(
action: {
// did tap
},
label: { Text("Click Me") }
)

If your Button's label is only Text you can initialize with this simpler signature.

Button("Click Me") {
// did tap
}

You can get a bit fancy with this button

Button(action: {

}, label: {
Image(systemName: "clock")
Text("Click Me")
Text("Subtitle")
})
.foregroundColor(Color.white)
.padding()
.background(Color.blue)
.cornerRadius(5)

Documentation - Button

iOS 14

Create a link-style Button that will open in the associated app, if possible, but otherwise in the user’s default web browser.

Link("View Our Terms of Service", destination: URL(string: "https://www.example.com/TOS.html")!)

Documentation - Link

NavigationLink

A button that triggers a navigation presentation when pressed. This is a replacement for pushViewController

NavigationView {
NavigationLink(destination:
Text("Detail")
.navigationBarTitle(Text("Detail"))
) {
Text("Push")
}.navigationBarTitle(Text("Master"))
}

Or make it more readable by use group destination into it own view DetailView

NavigationView {
NavigationLink(destination: DetailView()) {
Text("Push")
}.navigationBarTitle(Text("Master"))
}

Not sure if it is a bug or by design, in Beta 5 above code won't work. Try wrap NavigationLink in List like this to test the feature out.

NavigationView {
List {
NavigationLink(destination: Text("Detail")) {
Text("Push")
}.navigationBarTitle(Text("Master"))
}
}

If your NavigationLink's label is only Text you can initialize with this simpler signature.

NavigationLink("Detail", destination: Text("Detail").navigationBarTitle(Text("Detail")))

Documentation - NavigationLink

ToolbarItem

iOS 14

A model that represents an item which can be placed in the toolbar or navigation bar. This represents most properties in UINavigationItem

Add titleView.

NavigationView {
Text("SwiftUI").padding()
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("Title")
Button("Clickable Subtitle") { print("principle") }
}
}
}
}

Add leftBarButtonItem or leftBarButtonItems.

NavigationView {
Text("SwiftUI").padding()
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {

} label: {
Image(systemName: "square.and.pencil")
}

}
}
}

Add rightBarButtonItem or rightBarButtonItems.

NavigationView {
Text("SwiftUI").padding()
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button {

} label: {
Image(systemName: "square.and.pencil")
}

}
ToolbarItem(placement: .navigationBarTrailing) {
Button {

} label: {
Image(systemName: "square.and.pencil")
}

}
}
}

Documentation - ToolbarItem

Toggle

A control that toggles between on and off states.

@State var isShowing = true // toggle state

Toggle(isOn: $isShowing) {
Text("Hello World")
}

If your Toggle's label is only Text you can initialize with this simpler signature.

Toggle("Hello World", isOn: $isShowing)

Documentation - Toggle

iOS 14

A view that displays an embedded map interface.

Show map with a specified region

import MapKit

@State var region = MKCoordinateRegion(center: .init(latitude: 37.334722, longitude: -122.008889), latitudinalMeters: 300, longitudinalMeters: 300)

Map(coordinateRegion: $region)

You can control a map interaction by specify interactionModes (Use [] to disable all interactions).

struct PinItem: Identifiable {
let id = UUID()
let coordinate: CLLocationCoordinate2D
}

Map(coordinateRegion: $region,
interactionModes: [],
showsUserLocation: true,
userTrackingMode: nil,
annotationItems: [PinItem(coordinate: .init(latitude: 37.334722, longitude: -122.008889))]) { item in
MapMarker(coordinate: item.coordinate)
}

Documentation - Map

Picker

A control for selecting from a set of mutually exclusive values.

Picker style change based on its ancestor, under Form or List it appear as a single list row that you can tap to bring in a new screen showing all possible options.

NavigationView {
Form {
Section {
Picker(selection: $selection, label:
Text("Picker Name")
, content: {
Text("Value 1").tag(0)
Text("Value 2").tag(1)
Text("Value 3").tag(2)
Text("Value 4").tag(3)
})
}
}
}

You can override style with .pickerStyle(WheelPickerStyle()).

In SwiftUI, UISegmentedControl is just another style of Picker.

@State var mapChoioce = 0
var settings = ["Map", "Transit", "Satellite"]
Picker("Options", selection: $mapChoioce) {
ForEach(0 ..< settings.count) { index in
Text(self.settings[index])
.tag(index)
}

}.pickerStyle(SegmentedPickerStyle())

Segmented Control also got a new look in iOS 13

Documentation - Picker

DatePicker

A control for selecting an absolute date.

Date Picker style also changes based on its ancestor. Under Form or List, it appears as a single list row that you can tap to expand to date picker (just like calendar app).

@State var selectedDate = Date()

var dateClosedRange: ClosedRange<Date> {
let min = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
let max = Calendar.current.date(byAdding: .day, value: 1, to: Date())!
return min...max
}

NavigationView {
Form {
Section {
DatePicker(
selection: $selectedDate,
in: dateClosedRange,
displayedComponents: .date,
label: { Text("Due Date") }
)
}
}
}

Outside Form and List, it shows as normal wheel picker

@State var selectedDate = Date()

var dateClosedRange: ClosedRange<Date> {
let min = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
let max = Calendar.current.date(byAdding: .day, value: 1, to: Date())!
return min...max
}

DatePicker(
selection: $selectedDate,
in: dateClosedRange,
displayedComponents: [.hourAndMinute, .date],
label: { Text("Due Date") }
)

If your DatePicker's label is only plain Text you can initialize with this simpler signature.

DatePicker("Due Date",
selection: $selectedDate,
in: dateClosedRange,
displayedComponents: [.hourAndMinute, .date])

minimumDate and maximumDate can be set using ClosedRange, PartialRangeThrough, and PartialRangeFrom.

DatePicker("Minimum Date",
selection: $selectedDate,
in: Date()...,
displayedComponents: [.date])
DatePicker("Maximum Date",
selection: $selectedDate,
in: ...Date(),
displayedComponents: [.date])

Documentation - DatePicker

ProgressView

iOS 14

A view that shows the progress towards completion of a task.

@State private var progress = 0.5

VStack {
ProgressView(value: progress)
Button("More", action: { progress += 0.05 })
}

Use can use this as UIActivityIndicatorView by apply CircularProgressViewStyle.

ProgressView(value: progress)
.progressViewStyle(CircularProgressViewStyle())

Documentation - ProgressView

Slider

A control for selecting a value from a bounded linear range of values.

@State var progress: Float = 0

Slider(value: $progress, from: 0.0, through: 100.0, by: 5.0)

Slider lack of minimumValueImage and maximumValueImage, but we can replicate that easily by `HStack

@State var progress: Float = 0
HStack {
Image(systemName: "sun.min")
Slider(value: $progress, from: 0.0, through: 100.0, by: 5.0)
Image(systemName: "sun.max.fill")
}.padding()

Documentation - Slider

Stepper

A control used to perform semantic increment and decrement actions.

@State var quantity: Int = 0
Stepper(value: $quantity, in: 0...10, label: { Text("Quantity \(quantity)")})

If your Stepper's label is only Text you can initialize with this simpler signature.

Stepper("Quantity \(quantity)", value: $quantity, in: 0...10)

If you want full control, they offer bare bone Stepper where you manage your own data source.

@State var quantity: Int = 0
Stepper(onIncrement: {
self.quantity += 1
}, onDecrement: {
self.quantity -= 1
}, label: { Text("Quantity \(quantity)") })

If you also specify an amount of value for each step with initializers with step.

Stepper(value: $quantity, in: 0...10, step: 2) {
Text("Quantity \(quantity)")
}

Documentation - Stepper

SwiftUI - View Layout and Presentation

HStack

A view that arranges its children in a horizontal line.

To create static scrollable List

HStack (alignment: .center, spacing: 20){
Text("Hello")
Divider()
Text("World")
}

Documentation - HStack

LazyHStack

iOS 14

A view that arranges its children in a line that grows horizontally, creating items only as needed.

ScrollView(.horizontal) {
LazyHStack(alignment: .center, spacing: 20) {
ForEach(1...100, id: \.self) {
Text("Column \($0)")
}
}
}

Documentation - LazayHStack

VStack

A view that arranges its children in a vertical line.

To create static scrollable List

VStack (alignment: .center, spacing: 20){
Text("Hello")
Divider()
Text("World")
}

Documentation - VStack

LazyVStack

iOS 14

A view that arranges its children in a line that grows vertically, creating items only as needed.

ScrollView {
LazyVStack(alignment: .leading) {
ForEach(1...100, id: \.self) {
Text("Row \($0)")
}
}
}

Documentation - LazyVStack

ZStack

A view that overlays its children, aligning them in both axes.

ZStack {
Text("Hello")
.padding(10)
.background(Color.red)
.opacity(0.8)
Text("World")
.padding(20)
.background(Color.red)
.offset(x: 0, y: 40)
}
}

Documentation - ZStack

A container that presents rows of data arranged in a single column.

To create static scrollable List

List {
Text("Hello world")
Text("Hello world")
Text("Hello world")
}

Cell can be mixed

List {
Text("Hello world")
Image(systemName: "clock")
}

To create dynamic List

let names = ["John", "Apple", "Seed"]
List(names) { name in
Text(name)
}

To add section

List {
Section(header: Text("UIKit"), footer: Text("We will miss you")) {
Text("UITableView")
}

Section(header: Text("SwiftUI"), footer: Text("A lot to learn")) {
Text("List")
}
}

To make it grouped add .listStyle(GroupedListStyle())

List {
Section(header: Text("UIKit"), footer: Text("We will miss you")) {
Text("UITableView")
}

Section(header: Text("SwiftUI"), footer: Text("A lot to learn")) {
Text("List")
}
}.listStyle(GroupedListStyle())

To make it inset grouped (.insetGrouped), add .listStyle(GroupedListStyle()) and force regular horizontal size class .environment(\.horizontalSizeClass, .regular).

List {
Section(header: Text("UIKit"), footer: Text("We will miss you")) {
Text("UITableView")
}

Section(header: Text("SwiftUI"), footer: Text("A lot to learn")) {
Text("List")
}
}.listStyle(GroupedListStyle())
.environment(\.horizontalSizeClass, .regular)

Inset grouped was added to SwiftUI in iOS 13.2

iOS 14

In iOS 14, we have a dedicated style for this.

.listStyle(InsetGroupedListStyle())

Documentation - List

ScrollView

scroll view.

ScrollView(alwaysBounceVertical: true) {
Image("foo")
Text("Hello World")
}

Documentation - ScrollView

LazyHGrid

iOS 14

A container view that arranges its child views in a grid that grows horizontally, creating items only as needed.

var rows: [GridItem] =
Array(repeating: .init(.fixed(20)), count: 2)

ScrollView(.horizontal) {
LazyHGrid(rows: rows, alignment: .top) {
ForEach((0...100), id: \.self) {
Text("\($0)").background(Color.pink)
}
}
}

Documentation - LazyHGrid

LazyVGrid

iOS 14

A container view that arranges its child views in a grid that grows vertically, creating items only as needed.

var columns: [GridItem] =
Array(repeating: .init(.fixed(20)), count: 5)

ScrollView {
LazyVGrid(columns: columns) {
ForEach((0...100), id: \.self) {
Text("\($0)").background(Color.pink)
}
}
}

Documentation - LazyVGrid

A container for grouping controls used for data entry, such as in settings or inspectors.

You can put almost anything into this Form and it will render appropriate style for a form.

NavigationView {
Form {
Section {
Text("Plain Text")
Stepper(value: $quantity, in: 0...10, label: { Text("Quantity") })
}
Section {
DatePicker($date, label: { Text("Due Date") })
Picker(selection: $selection, label:
Text("Picker Name")
, content: {
Text("Value 1").tag(0)
Text("Value 2").tag(1)
Text("Value 3").tag(2)
Text("Value 4").tag(3)
})
}
}
}

Documentation - Form

Spacer

A flexible space that expands along the major axis of its containing stack layout, or on both axes if not contained in a stack.

HStack {
Image(systemName: "clock")
Spacer()
Text("Time")
}

Documentation - Spacer

Divider

A visual element that can be used to separate other content.

HStack {
Image(systemName: "clock")
Divider()
Text("Time")
}.fixedSize()

Documentation - Divider

NavigationView

A view for presenting a stack of views representing a visible path in a navigation hierarchy.

NavigationView {            
List {
Text("Hello World")
}
.navigationBarTitle(Text("Navigation Title")) // Default to large title style
}

For old style title

NavigationView {            
List {
Text("Hello World")
}
.navigationBarTitle(Text("Navigation Title"), displayMode: .inline)
}

Add UIBarButtonItem

NavigationView {
List {
Text("Hello World")
}
.navigationBarItems(trailing:
Button(action: {
// Add action
}, label: {
Text("Add")
})
)
.navigationBarTitle(Text("Navigation Title"))
}

Add show/push with NavigationLink

Use as UISplitViewController.

NavigationView {
List {
NavigationLink("Go to detail", destination: Text("New Detail"))
}.navigationBarTitle("Master")
Text("Placeholder for Detail")
}

You can style a NavigationView using two new style properties: stack and doubleColumn. By default, navigation views on iPhone and Apple TV visually reflect a navigation stack, while on iPad and Mac, a split-view styled navigation view displays.

You can override this with .navigationViewStyle.

NavigationView {
MyMasterView()
MyDetailView()
}
.navigationViewStyle(StackNavigationViewStyle())

iOS 14

In iOS 14, there is a new sidebar style for UISplitViewController. You can also do that by putting three views under NavigationView.

NavigationView {
Text("Sidebar")
Text("Primary")
Text("Detail")
}

Documentation - NavigationView

iOS 14

Add UIToolbar like toolbarItems in UIViewController.

NavigationView {
Text("SwiftUI").padding()
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button {

} label: {
Image(systemName: "archivebox")
}
}

ToolbarItem(placement: .bottomBar) {
Spacer()
}

ToolbarItem(placement: .bottomBar) {
Button {

} label: {
Image(systemName: "square.and.pencil")
}
}
}
}

Documentation - ToolbarItem

TabView

A view that allows for switching between multiple child views using interactable user interface elements.

TabView {
Text("First View")
.font(.title)
.tabItem({ Text("First") })
.tag(0)
Text("Second View")
.font(.title)
.tabItem({ Text("Second") })
.tag(1)
}

Image and Text together. You can use SF Symbol here.

TabView {
Text("First View")
.font(.title)
.tabItem({
Image(systemName: "circle")
Text("First")
})
.tag(0)
Text("Second View")
.font(.title)
.tabItem(VStack {
Image("second")
Text("Second")
})
.tag(1)
}

Or you can omit VStack

TabView {
Text("First View")
.font(.title)
.tabItem({
Image(systemName: "circle")
Text("First")
})
.tag(0)
Text("Second View")
.font(.title)
.tabItem({
Image("second")
Text("Second")
})
.tag(1)
}

UIPageViewController

UIPageViewController become a style of TabView. To use page view style, use .tabViewStyle(PageTabViewStyle()) modifier on your tab view.

TabView {
Text("1")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.pink)
Text("2")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
Text("3")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
Text("4")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)
}.tabViewStyle(PageTabViewStyle())

This PageTabViewStyle will include UIPageControl at the bottom just like UIPageViewController. To remove it, pass indexDisplayMode to PageTabViewStyle.

.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))

There is a new style for page control which will render background around the indicator. To enforce this new style, add .indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always)) modifier to your tab view.

TabView {
Text("1")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.pink)
Text("2")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red)
Text("3")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.green)
Text("4")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue)
}
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.tabViewStyle(PageTabViewStyle())

Documentation - TabView

Alert

A container for an alert presentation.

We can show Alert based on boolean.

@State var isError: Bool = false

Button("Alert") {
self.isError = true
}.alert(isPresented: $isError, content: {
Alert(title: Text("Error"), message: Text("Error Reason"), dismissButton: .default(Text("OK")))
})

It is also bindable with Identifiable item.

@State var error: AlertError?

var body: some View {
Button("Alert Error") {
self.error = AlertError(reason: "Reason")
}.alert(item: $error, content: { error in
alert(reason: error.reason)
})
}

func alert(reason: String) -> Alert {
Alert(title: Text("Error"),
message: Text(reason),
dismissButton: .default(Text("OK"))
)
}

struct AlertError: Identifiable {
var id: String {
return reason
}

let reason: String
}

Documentation - Alert

Modal

A modal transition.

We can show Modal based on boolean.

@State var isModal: Bool = false

var modal: some View {
Text("Modal")
}

Button("Modal") {
self.isModal = true
}.sheet(isPresented: $isModal, content: {
self.modal
})

Documentation - Sheet

It is also bindable with Identifiable item.

@State var detail: ModalDetail?

var body: some View {
Button("Modal") {
self.detail = ModalDetail(body: "Detail")
}.sheet(item: $detail, content: { detail in
self.modal(detail: detail.body)
})
}

func modal(detail: String) -> some View {
Text(detail)
}

struct ModalDetail: Identifiable {
var id: String {
return body
}

let body: String
}

Documentation - Sheet

iOS 14

If you want the old style of modal presentation where a modal view is presented full screen, you can use .fullScreenCover instead of .sheet.

Since the full-screen cover style doesn't allow a user to use gesture to dismiss the modal, you have to add a way to dismiss the presented view manually. In the following example, we add a button to dismiss the presented view by set isModal to false.

@State var isModal: Bool = false

var modal: some View {
Text("Modal")
Button("Dismiss") {
self.isModal = false
}
}

Button("Fullscreen") {
self.isModal = true
}.fullScreenCover(isPresented: $isFullscreen, content: {
self.modal
})

If you use a custom view as a modal, you can dismiss the presented view using the presentationMode environmental key.

struct Modal: View {
@Environment(\.presentationMode) var presentationMode

var body: some View {
Text("Modal")
Button("Dismiss Modal") {
presentationMode.wrappedValue.dismiss()
}
}
}

struct ContentView: View {
@State private var isModal = false

var body: some View {
Button("Fullscreen") {
isModal = true
}
.fullScreenCover(isPresented: $isFullscreen, content: {
Modal()
})
}

Documentation - fullScreenCover

ActionSheet

A storage type for an action sheet presentation.

We can show ActionSheet based on boolean.

@State var isSheet: Bool = false

var actionSheet: ActionSheet {
ActionSheet(title: Text("Action"),
message: Text("Description"),
buttons: [
.default(Text("OK"), action: {

}),
.destructive(Text("Delete"), action: {

})
]
)
}

Button("Action Sheet") {
self.isSheet = true
}.actionSheet(isPresented: $isSheet, content: {
self.actionSheet
})

It is also bindable with Identifiable item.

@State var sheetDetail: SheetDetail?

var body: some View {
Button("Action Sheet") {
self.sheetDetail = ModSheetDetail(body: "Detail")
}.actionSheet(item: $sheetDetail, content: { detail in
self.sheet(detail: detail.body)
})
}

func sheet(detail: String) -> ActionSheet {
ActionSheet(title: Text("Action"),
message: Text(detail),
buttons: [
.default(Text("OK"), action: {

}),
.destructive(Text("Delete"), action: {

})
]
)
}

struct SheetDetail: Identifiable {
var id: String {
return body
}

let body: String
}

Documentation - ActionSheet

Framework Integration - UIKit in SwiftUI

UIViewRepresentable

A view that represents a UIKit view. Use this when you want to use UIView inside SwiftUI.

To make any UIView usable in SwiftUI, create a wrapper view that conforms UIViewRepresentable.

import UIKit
import SwiftUI

struct ActivityIndicator: UIViewRepresentable {
@Binding var isAnimating: Bool

func makeUIView(context: Context) -> UIActivityIndicatorView {
let v = UIActivityIndicatorView()

return v
}

func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
if isAnimating {
uiView.startAnimating()
} else {
uiView.stopAnimating()
}
}
}

If you want to bridge between UIKit data binding (delegate, target/action) and SwiftUI, use Coordinator. Detail can be found in SwiftUI tutorials

import SwiftUI
import UIKit

struct PageControl: UIViewRepresentable {
var numberOfPages: Int
@Binding var currentPage: Int

func makeUIView(context: Context) -> UIPageControl {
let control = UIPageControl()
control.numberOfPages = numberOfPages
control.addTarget(
context.coordinator,
action: #selector(Coordinator.updateCurrentPage(sender:)),
for: .valueChanged)

return control
}

func updateUIView(_ uiView: UIPageControl, context: Context) {
uiView.currentPage = currentPage
}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

// This is where old paradigm located
class Coordinator: NSObject {
var control: PageControl

init(_ control: PageControl) {
self.control = control
}

@objc func updateCurrentPage(sender: UIPageControl) {
control.currentPage = sender.currentPage
}
}
}

Documentation - UIViewRepresentable

UIViewControllerRepresentable

A view that represents a UIKit view controller. Use this when you want to use UIViewController inside SwiftUI.

To make any UIViewController usable in SwiftUI, create a wrapper view that conforms UIViewControllerRepresentable. Detail can be found in SwiftUI tutorials


import SwiftUI
import UIKit

struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]

func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)

return pageViewController
}

func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[controllers[0]], direction: .forward, animated: true)
}
}

Documentation - UIViewControllerRepresentable

Framework Integration - SwiftUI in UIKit

UIHostingController

A UIViewController that represents a SwiftUI view.

let vc = UIHostingController(rootView: Text("Hello World"))
let vc = UIHostingController(rootView: ContentView())

Documentation - UIHostingController

App Structure and Behavior

iOS 14

A type that represents the structure and behavior of an app.

import SwiftUI

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
Text("Hello, world!")
}
}
}

You can also create custom scenes. To include more than one scene in an app, add the @SceneBuilder attribute to the body.

@main
struct Mail: App {
@SceneBuilder var body: some Scene {
WindowGroup {
MailViewer()
}
Settings {
SettingsView()
}
}
}

Documentation - App

Scene

iOS 14

A part of an app’s user interface with a life cycle managed by the system.

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
Text("Hello, world!")
}
}
}

Documentation - Scene


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK