NavigationStack
import SwiftUI struct NavigationStackBootcamp : View { let fruits = [ "Apple" , "Orange" , "Banana" ] @State private var stackPath: [ String ] = [ ] var body: some View { NavigationStack ( path: $stackPath) { VStack { Button ( "stackPathButton" ) { stackPath. append ( contentsOf: [ "xxx " , "yyy" , "zzz" ] ) } } . navigationTitle ( "这是一个标题" ) . navigationDestination ( for : Int . self ) { item in MySecondViewScreen ( index: item) } . navigationDestination ( for : String . self ) { item in Text ( "next view: \( item ) " ) } } Divider ( ) NavigationStack ( ) { ScrollView { VStack ( spacing: 40 ) { ForEach ( fruits, id: \ . self ) { fruit in NavigationLink ( value: fruit) { Text ( fruit) } } ForEach ( 0 ... 20 , id: \ . self ) { item in NavigationLink ( value: item) { Text ( "current view: \( item ) " ) }
} } } . navigationTitle ( "这是一个标题" ) . navigationDestination ( for : Int . self ) { item in MySecondViewScreen ( index: item) } . navigationDestination ( for : String . self ) { item in Text ( "next view: \( item ) " ) } } }
} struct MySecondViewScreen : View { var index: Int init ( index: Int ) { self . index = indexprint ( "Init Screen: \( index ) " ) } var body: some View { Text ( "next view: \( index ) " ) }
} #Preview { NavigationStackBootcamp ( )
}
Toolbar
import SwiftUI struct ToolbarBootcamp : View { @State private var textFieldString: String = "" @State private var paths: [ String ] = [ ] var body: some View { NavigationStack ( path: $paths) { ZStack { Color . white. ignoresSafeArea ( ) ScrollView { TextField ( "Placeholder" , text: $textFieldString) Text ( "你好~" ) . font ( . largeTitle) . foregroundStyle ( . white) ForEach ( 0 ..< 50 ) { _ in Rectangle ( ) . frame ( width: 200 , height: 200 ) . cornerRadius ( 10 ) . foregroundColor ( . blue) } } } . navigationTitle ( "导航标题" )
. toolbar ( content: { ToolbarItem ( placement: . topBarLeading) { Image ( systemName: "heart.fill" ) } ToolbarItem ( placement: . principal) { Image ( systemName: "gear" ) } ToolbarItem ( placement: . topBarTrailing) { HStack { Image ( systemName: "heart.fill" ) Image ( systemName: "heart" ) } } ToolbarItem ( placement: . keyboard) { Image ( systemName: "gear" ) } ToolbarItem ( placement: . bottomBar) { Image ( systemName: "gear" ) . background ( . red) . frame ( maxWidth: . infinity, alignment: . leading) } } ) . navigationBarTitleDisplayMode ( . inline) . toolbarTitleMenu { Button ( "按钮1" ) { paths. append ( "按钮1" ) } Button ( "按钮2" ) { paths. append ( "按钮2" ) } } . navigationDestination ( for : String . self ) { value in Text ( "value: \( value ) " ) } } }
} #Preview { ToolbarBootcamp ( )
}
ResizableSheet
import SwiftUI struct ResizableSheetBootcamp : View { @State private var showSheet: Bool = false @State private var detents: PresentationDetent = . largevar body: some View { Button ( "Click Me" ) { showSheet. toggle ( ) } . sheet ( isPresented: $showSheet, content: { MyNextView ( detents: $detents) . presentationDetents ( [ . large, . medium] ) . presentationDragIndicator ( . hidden)
. presentationDetents ( [ . fraction ( 0.9 ) ] ) . presentationDetents ( [ . height ( 100 ) ] ) . presentationDetents ( [ . medium, . large, . fraction ( 0.1 ) ] , selection: $detents) } ) }
} struct MyNextView : View { @Binding var detents: PresentationDetent var body: some View { ZStack { Color . red. ignoresSafeArea ( ) VStack { Text ( "Hello World~" ) Button ( "Medim" ) { detents = . medium} Button ( "Large" ) { detents = . large} Button ( "fraction(0.1)" ) { detents = . fraction ( 0.1 ) } } } . onDisappear ( perform: { detents = . large} ) } } #Preview { ResizableSheetBootcamp ( )
}
SafeAreaInset
import SwiftUI struct SafeAreaInsetBootcamp : View { var body: some View { NavigationStack { List ( 0 ..< 10 ) { _ in Rectangle ( ) . foregroundColor ( . blue) . frame ( height: 300 ) . listRowBackground ( Color . green) } . navigationTitle ( "Safe Area Inset" ) . safeAreaInset ( edge: . top, alignment: . trailing) { Text ( "Hi1" ) . frame ( maxWidth: . infinity) . background ( . yellow) } . safeAreaInset ( edge: . bottom, alignment: . trailing) { Text ( "Hi2" ) . padding ( ) . background ( . yellow) . clipShape ( Circle ( ) ) . padding ( ) }
. safeAreaInset ( edge: . bottom, alignment: . center) { Text ( "Hi3" ) . frame ( maxWidth: . infinity) . background ( . yellow) } } }
} #Preview { SafeAreaInsetBootcamp ( )
}
Group
import SwiftUI struct GroupBootcamp : View { var body: some View { VStack ( spacing: 50 ) { Text ( "Hello, World!" ) Group { Text ( "Hello, World!" ) Text ( "Hello, World!" ) } . font ( . subheadline) . foregroundStyle ( . green) } . foregroundColor ( . red) . font ( . largeTitle) } } #Preview { GroupBootcamp ( )
}
AnimationUpdate
import SwiftUI struct AnimationUpdateBootcamp : View { @State private var animate1: Bool = false @State private var animate2: Bool = false var body: some View { ZStack { VStack { Button ( "Action 1" ) { animate1. toggle ( ) } Button ( "Action 2" ) { animate2. toggle ( ) } ZStack { Rectangle ( ) . foregroundColor ( . blue) . frame ( width: 100 , height: 100 ) . frame ( maxWidth: . infinity, alignment: animate1 ? . leading : . trailing) . background ( . green) . frame ( maxHeight: . infinity, alignment: animate2 ? . top : . bottom) . background ( . orange) } . frame ( maxWidth: . infinity, maxHeight: . infinity) . background ( . red) } } . animation ( . spring, value: animate1) }
} #Preview { AnimationUpdateBootcamp ( )
}
Menu
import SwiftUI struct MenuBootcamp : View { var body: some View { VStack ( spacing: 50 ) { Menu ( "Click Me" ) { Button ( "1" ) { } Button ( "2" ) { } Button ( "3" ) { } Button ( "4" ) { } } Menu ( "Click Me" ) { ControlGroup ( "group" ) { Button ( "1" ) { } Button ( "2" ) { } Button ( "3" ) { } } Button ( "4" ) { } Menu ( "Click Me" ) { ControlGroup ( "group2" ) { Button ( "1" ) { } Button ( "2" ) { } Button ( "3" ) { } } } } } }
} #Preview { MenuBootcamp ( )
}
NativePopover
import SwiftUI struct NativePopoverBootcamp : View { @State private var showPopover1: Bool = false @State private var showPopover2: Bool = false @State private var showPopover3: Bool = false @State private var showPopover4: Bool = false @State private var showPopover5: Bool = false private var array: [ String ] = [ "123" , "456" , "789" ] var body: some View { ZStack { Color . green. ignoresSafeArea ( ) ZStack { VStack ( spacing: 50 ) { Button ( "Click Me .sheet" ) { showPopover1. toggle ( ) } Button ( "Click Me .fullScreenCover" ) { showPopover2. toggle ( ) } Button ( "Click Me .popover" ) { showPopover3. toggle ( ) } Button ( "Click Me .popover" ) { showPopover4. toggle ( ) } Button ( "Click Me .popover" ) { showPopover5. toggle ( ) } . padding ( . bottom, - 100 ) } . background ( . red) . popover ( isPresented: $showPopover1, content: { Text ( "Hello Next View" ) . presentationCompactAdaptation ( PresentationAdaptation . sheet) } ) . popover ( isPresented: $showPopover2, content: { Text ( "Hello Next View" ) . presentationCompactAdaptation ( PresentationAdaptation . fullScreenCover) } ) . popover ( isPresented: $showPopover3, content: { Text ( "Hello Next View" ) . presentationCompactAdaptation ( PresentationAdaptation . popover) } ) . popover ( isPresented: $showPopover4, attachmentAnchor: . point ( UnitPoint . center) , arrowEdge: . top, content: { ScrollView { VStack ( alignment: . leading, spacing: 12 , content: { ForEach ( 0 ..< array. count) { item in Button ( array[ item] ) { } . foregroundStyle ( . black) if item != array. count - 1 { Divider ( ) } } } ) . padding ( 20 ) } . presentationCompactAdaptation ( PresentationAdaptation . popover) } ) . popover ( isPresented: $showPopover5, attachmentAnchor: . point ( UnitPoint . bottom) , arrowEdge: . top, content: { Text ( "Hello Next View" ) . presentationCompactAdaptation ( PresentationAdaptation . popover) } ) } } }
} #Preview { NativePopoverBootcamp ( )
}
AnyLayout
import SwiftUI struct AnyLayoutBootcamp : View { @Environment ( \ . horizontalSizeClass) private var horizontalSizeClass@Environment ( \ . verticalSizeClass) private var verticalSizeClassvar body: some View { VStack ( spacing: 12 , content: { Text ( "HorizontalSizeClass: \( horizontalSizeClass. debugDescription ) " ) Text ( "VerticalSizeClass: \( verticalSizeClass. debugDescription ) " ) } ) if horizontalSizeClass == . compact { VStack { Text ( "1111111111" ) Text ( "2222222222" ) Text ( "33333333333" ) } } else { HStack { Text ( "1111111111" ) Text ( "2222222222" ) Text ( "33333333333" ) } } let layout: AnyLayout = horizontalSizeClass == . compact ? AnyLayout ( VStackLayout ( ) ) : AnyLayout ( HStackLayout ( ) ) layout{ Text ( "1111111111" ) Text ( "2222222222" ) Text ( "33333333333" ) } }
} #Preview { AnyLayoutBootcamp ( )
}
ViewThatFits
import SwiftUI struct ViewThatFitsBootcamp : View { var body: some View { ZStack { Color . red. ignoresSafeArea ( ) VStack { Text ( "123456789012345678901234567890" ) Divider ( ) Text ( "123456789012345678901234567890" ) . lineLimit ( 1 ) . minimumScaleFactor ( 0.1 ) Divider ( ) ViewThatFits { Text ( "123456789012345678901234567890" ) Text ( "12345678901234567" ) Text ( "12345678" ) } } } . frame ( height: 300 ) . padding ( 20 ) . font ( . largeTitle) }
} #Preview { ViewThatFitsBootcamp ( )
}
NavigationSplitView
import SwiftUI
struct NavigationSplitViewBootcamp : View { @State private var visibility: NavigationSplitViewVisibility = . detailOnlyvar body: some View {
NavigationSplitView { Color . red} content: { Color . green} detail: { Color . blue} NavigationSplitView ( columnVisibility: $visibility) { Color . red} content: { Color . green} detail: { ZStack ( alignment: . topLeading) { Color . blueText ( "123" ) . padding ( . leading, 10 ) . padding ( . top, 15 ) } } . navigationSplitViewStyle ( . prominentDetail) }
} #Preview { NavigationSplitViewBootcamp ( )
}
GridView
import SwiftUI struct GridViewBootcamp : View { var body: some View { ScrollView ( ) { Grid ( ) { GridRow { cell ( int: 1 ) cell ( int: 2 ) cell ( int: 3 ) } Divider ( ) . frame ( height: 10 ) . gridCellUnsizedAxes ( . horizontal) . background ( . blue) GridRow { cell ( int: 4 ) cell ( int: 5 ) } cell ( int: 6 ) cell ( int: 7 ) cell ( int: 8 ) } . background ( . gray) Grid ( ) { ForEach ( 0 ..< 4 ) { rowIndex in GridRow { ForEach ( 0 ..< 4 ) { columnIndex in let cellNumber = ( columnIndex + 1 ) + ( rowIndex * 4 ) cell ( int: cellNumber) } } } } . background ( . purple) Grid ( alignment: . trailing, horizontalSpacing: 5 , verticalSpacing: 20 , content: { ForEach ( 0 ..< 4 ) { rowIndex in GridRow { ForEach ( 0 ..< 4 ) { columnIndex in let cellNumber = ( columnIndex + 1 ) + ( rowIndex * 4 ) cell ( int: cellNumber) } } } } ) . background ( . yellow) Grid ( alignment: . trailing, horizontalSpacing: 5 , verticalSpacing: 20 , content: { ForEach ( 0 ..< 4 ) { rowIndex in GridRow { ForEach ( 0 ..< 4 ) { columnIndex in let cellNumber = ( columnIndex + 1 ) + ( rowIndex * 4 ) if cellNumber == 7 { Color . clear. gridCellUnsizedAxes ( [ . horizontal, . vertical] ) } else { cell ( int: cellNumber) } } } } } ) . background ( . blue) Grid ( alignment: . center, horizontalSpacing: 5 , verticalSpacing: 20 , content: { ForEach ( 0 ..< 4 ) { rowIndex in GridRow { ForEach ( 0 ..< 4 ) { columnIndex in let cellNumber = ( columnIndex + 1 ) + ( rowIndex * 4 ) if cellNumber == 7 { EmptyView ( ) } else { cell ( int: cellNumber) . gridCellColumns ( cellNumber == 6 ? 2 : 1 ) . gridColumnAlignment ( cellNumber == 6 ? . center : . trailing) } } } } } ) . background ( Color . brown) } } private func cell ( int: Int ) -> some View { Text ( " \( int ) " ) . font ( . largeTitle) . padding ( ) . background ( . orange) }
} #Preview { GridViewBootcamp ( )
}
ContentUnavailableView
import SwiftUI struct ContentUnavailableViewBootcamp : View { var body: some View { ContentUnavailableView ( "暂无网络" , systemImage: "wifi.slash" , description: Text ( "请检查网络,刷新后再试~😄" ) ) ContentUnavailableView . search}
} #Preview { ContentUnavailableViewBootcamp ( )
}
ObservableBootcamp
iOS之前的做法:
import SwiftUI private class ObservableViewModel : ObservableObject { @Published var title: String = "Some title"
} private struct ObservableBootcamp : View { @StateObject private var viewModel = ObservableViewModel ( ) var body: some View { VStack ( spacing: 40 ) { Button ( viewModel. title) { viewModel. title = "new title~" } . font ( . largeTitle) SomeChildView ( viewModel: viewModel) SomeThirdChildView ( ) } . environmentObject ( viewModel) }
} private struct SomeChildView : View { @ObservedObject var viewModel: ObservableViewModel var body: some View { Button ( viewModel. title) { viewModel. title = "subView title~" } . font ( . largeTitle) }
} private struct SomeThirdChildView : View { @EnvironmentObject var viewModel: ObservableViewModel var body: some View { Button ( viewModel. title) { viewModel. title = "thirdChildView title~" } . font ( . largeTitle) }
} #Preview { ObservableBootcamp ( )
}
iOS17的做法
import SwiftUI
@Observable private class ObservableViewModel { var title: String = "Some title"
} private struct ObservableBootcampIOS17 : View { @State private var viewModel = ObservableViewModel ( ) var body: some View { VStack ( spacing: 40 ) { Button ( viewModel. title) { viewModel. title = "new title~" } . font ( . largeTitle) SomeChildView ( viewModel: viewModel) SomeThirdChildView ( ) } . environment ( viewModel) }
} private struct SomeChildView : View { @Bindable var viewModel: ObservableViewModel var body: some View { Button ( viewModel. title) { viewModel. title = "subView title~" } . font ( . largeTitle) }
} private struct SomeThirdChildView : View { @Environment ( ObservableViewModel . self ) var viewModelvar body: some View { Button ( viewModel. title) { viewModel. title = "thirdChildView title~" } . font ( . largeTitle) }
} #Preview { ObservableBootcampIOS17 ( )
}