#development #ios

idea

Refer to training[1]

Components

Element Description Example
Text Simple text
Label Text + icon Label("title", systemImage: "person.3")
Spacer Expends to push other items to borders
List Menu-like list of options, used for navigation
Section Creates a separator in a list with a title
H/VStack Arrange elements horizontally/vertically
Button A button
Form Edit data
Picker Pick one option

Done with NavigationStack and NavigationLinks.

The title to each step of the NavigationStack is set by add a .navigationTitle on elements of the stack.

Example:

    List {
    // [...]
    }
          .navigationTitle(nonsense.excuse)

State

@State wrapper makes a property mutable in the view it's defined in. Allows SwiftUI to update the values of that property.

@Binding wrapper identifies a property as mutable in a parent (It makes it into a Binding<T>)

Bindings (only to edit) are then passed through $ to components for edition. Eg: TextField("Some variable", text: $variableName)

Adding fields is done with an animation in the Button's action

action: {
    withAnimation {
        myList.add(something)
        something = ""
    }
}

Removing is done by adding a ondelete to the ForEach.

.onDelete { indices in
    torture.victims.remove(atOffsets: indices)
}

To pass a constant as binding, wrap it in .constant(value). If you pass as a constant, then the binding will be constant in the entire hierarchy. To pass the binding from the app:

@State private var data = DailyMadness.sampleData
    var body: some Scene {
        WindowGroup {
            SuppliceListView(supplices: $data)
        }
    }

@StateObject creates a singleton instance of an object, marks as state for the lifecycle of the view, and makes it observable.

Add style as leading dot syntax

Declare a style.

Declare an extension for the super-type, with the following syntax: (example for LabelStyle):

extension LabelStyle where Self == TrailingIconLabelStyle {
    static var trailingIcon: Self { Self() }
}

Modals

Modals are done for short, out of flow tasks, such as editing an object. They're triggered with sheet on a view.

        .sheet(isPresented: $isPresentingEditView) {
            TortureEditView()
        }

Scenes

See App Essentials[4]

Async & concurrency

Async functions are declared with

func fetchParticipants() async -> [Participant] { return [] }

Call with await. Can only be called from an async context. An async context is created with

Task {
      await fetchParticipants()
}

Lifetime of a task matches that of the view. When the view disappears, tasks are cancelled[5].

@State and @Binding properties (and UI) can only be mutated from the main thread. To interact with main thread, use annotation @MainActor on the class, and @Published on the variable that need to be mutated with the main thread.

Async tasks can be invoked from views using .task {}

Custom shapes

Create a custom shape by implementing :Shape.

    func path(in rect: CGRect) -> Path {
        let radius = (min(rect.size.height, rect.size.width) - CGFloat(diameter)) / 2.0
        let center = CGPoint(x: rect.midX, y: rect.midY)
        return Path { path in
            path.addArc(center: center, radius: radius, startAngle: Angle(), endAngle: currentAngle, clockwise: false)
        }
    }

ref

[1]: Creating a card view — iOS App Dev Tutorials | Apple Developer Documentation

[2]: Modality | Apple Developer Documentation

[3]: Creating the edit view — iOS App Dev Tutorials | Apple Developer Documentation

[4]: App essentials in SwiftUI - WWDC20 - Videos - Apple Developer

[5]: Adopting Swift concurrency — iOS App Dev Tutorials | Apple Developer Documentation

https://github.com/cfe84/exp-ios-scrumsucks