NavigationStack
NavigationView删除后的替代方法
NavigationStack {
List(1..<50) { i in
NavigationLink {
Text("Detail \(i)")
} label: {
Label("Row \(i)", systemImage: "\(i).circle")
}
}
.navigationTitle("Navigation")
}
NavigationStack {
List(1..<50) { i in
NavigationLink(value: i) {
Label("Row \(i)", systemImage: "\(i).circle")
}
}
.navigationDestination(for: Int.self, destination: { i in
Text("Detail \(i)")
})
.navigationTitle("Navigation")
}
struct ContentView: View {
@State private var persentedNumbers = [2, 3, 5]
var body: some View {
NavigationStack(path: $persentedNumbers) {
List(1..<50) { i in
NavigationLink(value: i) {
Label("Row \(i)", systemImage: "\(i).circle")
}
}
.navigationDestination(for: Int.self, destination: { i in
Text("Detail \(i)")
})
.navigationTitle("Navigation")
}
}
}
struct ContentView: View {
@State private var persentedNumbers = NavigationPath()
var body: some View {
NavigationStack(path: $persentedNumbers) {
NavigationLink(value: "Example String") {
Text("Tap Me")
}
List(1..<50) { i in
NavigationLink(value: i) {
Label("Row \(i)", systemImage: "\(i).circle")
}
}
.navigationDestination(for: Int.self, destination: { i in
Text("Detail \(i)")
})
.navigationDestination(for: String.self, destination: { s in
Text("String is: \(s)")
})
.navigationTitle("Navigation")
}
}
}
struct ContentView: View {
@State private var players = ["Sinduke", "Jianshu", "Tom"]
@State private var selectedPlayer: String?
var body: some View {
NavigationSplitView {
List(players, id: \.self, selection: $selectedPlayer, rowContent: Text.init)
} detail: {
Text(selectedPlayer ?? "Please choose a player")
}
}
}
struct Team: Identifiable, Hashable {
let id = UUID()
var name: String
var players: [String]
}
struct ContentView: View {
@State private var teams = [
Team(name: "SKT T1", players: ["Faker", "Gumayusi", "Zeus", "Oner", "Keria"])
]
@State private var selectedTeam: Team?
@State private var selectedPlayer: String?
var body: some View {
NavigationSplitView {
List(teams, selection: $selectedTeam) { team in
Text(team.name).tag(team)
}
.navigationSplitViewColumnWidth(250)
} content: {
List(selectedTeam?.players ?? [], id: \.self, selection: $selectedPlayer, rowContent: Text.init)
} detail: {
Text(selectedPlayer ?? "Please choose a player")
}
.navigationSplitViewStyle(.prominentDetail)
}
}
Sheet
struct ContentView: View {
@State private var showingCredits = false
var body: some View {
Button("Show Credits") {
showingCredits.toggle()
}
.sheet(isPresented: $showingCredits) {
Text("This is Credits Content, This is Credits Content")
// .presentationDetents([.height(300)])
// .presentationDetents([.medium, .large])
// .presentationDragIndicator(.hidden)
.presentationDetents([.fraction(0.5)])
}
}
}
Grid
struct ContentView: View {
var body: some View {
Grid{
GridRow {
Text("Top Leading")
.padding()
.background(.red)
Text("Top Trailing")
.padding()
.background(.orange)
}
GridRow {
Text("Bottom Leading")
.padding()
.background(.blue)
Text("Bottom Trailing")
.padding()
.background(.green)
}
}
}
}
struct ContentView: View {
@State private var redScore = 0
@State private var blueScore = 0
var body: some View {
Grid{
GridRow {
Text("Red")
ForEach(0..<redScore, id: \.self) { _ in
Rectangle()
.fill(.red)
.frame(width: 20, height: 20)
}
}
GridRow {
Text("Blue")
ForEach(0..<blueScore, id: \.self) { _ in
Rectangle()
.fill(.blue)
.frame(width: 20, height: 20)
}
}
Button("Red Scored") { redScore += 1 }
Button("Blue Scored") { blueScore += 1 }
}
}
}
struct ContentView: View {
@State private var redScore = 0
@State private var blueScore = 0
var body: some View {
Grid {
GridRow {
Text("Food")
Text("$200")
}
GridRow {
Text("Rent")
Text("$800")
}
GridRow {
Text("Candles")
Text("$3600")
}
Divider()
GridRow {
Text("$4600")
.gridCellColumns(2)
.multilineTextAlignment(.trailing)
}
}
}
}
OnTapGesture
struct ContentView: View {
var body: some View {
Circle()
.fill(.red.gradient)
.frame(width: 100, height: 100)
.onTapGesture { location in
debugPrint("Tapped at \(location)")
}
}
}
struct ContentView: View {
var body: some View {
Circle()
.fill(.red.gradient)
.frame(width: 100, height: 100)
.onTapGesture(coordinateSpace: .global) { location in
debugPrint("Tapped at \(location)")
}
}
}
分享链接
struct ContentView: View {
var body: some View {
let link = URL(string: "https://www.google.com")!
VStack(spacing: 20.0) {
ShareLink(item: link)
ShareLink("Learn Swift here", item: link)
ShareLink(item: link) {
Label("Learn Swift here", systemImage: "swift")
}
ShareLink(item: link, subject: Text("Subject test"), message: Text("This is the world search site"))
}
}
}
struct ContentView: View {
var body: some View {
let link = URL(string: "https://www.google.com")!
ShareLink(
item: link,
preview: SharePreview(
"string here",
image: Image(systemName: "plus")
)
)
}
}
MultiDatePicker
struct ContentView: View {
@State private var dates = Set<DateComponents>()
@Environment(\.calendar) var calendar
var body: some View {
VStack(spacing: 12) {
MultiDatePicker("Select your preferred dates", selection: $dates)
Text(summary)
}
.padding()
}
var summary: String {
dates.compactMap { components in
calendar.date(from: components)?.formatted(date: .long, time: .omitted)
}.formatted()
}
}
struct ContentView: View {
@State private var dates = Set<DateComponents>()
@Environment(\.calendar) var calendar
var body: some View {
VStack(spacing: 12) {
MultiDatePicker("Select your preferred dates", selection: $dates, in: Date.now...)
Text(summary)
}
.padding()
}
var summary: String {
dates.compactMap { components in
calendar.date(from: components)?.formatted(date: .long, time: .omitted)
}.formatted()
}
}
可搜索范围(Searchable scopes)
struct Message: Identifiable, Codable {
let id: Int
var user: String
var text: String
}
enum SearchScope: String, CaseIterable {
case inbox, favorites
}
struct ContentView: View {
@State private var messages = [Message]()
@State private var searchText = ""
@State private var searchScope = SearchScope.inbox
@State var useTime: String = "1212"
var body: some View {
NavigationStack {
List {
ForEach(filteredMessages) { message in
VStack(alignment: .leading) {
Text(message.user)
.font(.headline)
Text(message.text)
}
}
}
.searchable(text: $searchText)
.searchScopes($searchScope) {
ForEach(SearchScope.allCases, id: \.self) { scope in
Text(scope.rawValue.capitalized)
}
}
.navigationTitle("Messages")
}
.onSubmit(of: .search, runSearch)
.onChange(of: searchScope) { _ in
runSearch()
}
}
var filteredMessages: [Message] {
if searchText.isEmpty {
return messages
} else {
return messages.filter { $0.text.localizedCaseInsensitiveContains(searchText) }
}
}
func runSearch() {
Task {
let startTime = CFAbsoluteTimeGetCurrent()
guard let url = URL(string: "https://hws.dev/\(searchScope.rawValue).json") else { return }
let (data, _) = try await URLSession.shared.data(from: url)
messages = try JSONDecoder().decode([Message].self, from: data)
let endTime = CFAbsoluteTimeGetCurrent()
self.useTime = String(endTime - startTime)
debugPrint(useTime)
}
}
}
Gradient
struct ContentView: View {
let colors: [Color] = [.blue, .mint, .yellow, .green, .orange, .red, .purple, .indigo]
var body: some View {
VStack {
ForEach(colors, id: \.self) { color in
Rectangle()
.fill(color.gradient)
}
}
}
}
Shadow
struct ContentView: View {
var body: some View {
VStack {
Circle()
.fill(.mint.gradient.shadow(.drop(color: .black, radius: 20)))
.padding()
Circle()
.fill(.mint.gradient.shadow(.inner(color: .black, radius: 20)))
.padding()
}
}
}
AnyLayout
struct ContentView: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
var body: some View {
let layout = horizontalSizeClass == .regular ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
layout {
Image(systemName: "1.circle")
Image(systemName: "2.circle")
Image(systemName: "3.circle")
}
.font(.largeTitle)
}
}
struct ContentView: View {
@Environment(\.dynamicTypeSize) var dynamicTypeSize
var body: some View {
let layout = dynamicTypeSize <= .xxxLarge ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout())
layout {
Image(systemName: "1.circle")
Image(systemName: "2.circle")
Image(systemName: "3.circle")
}
.font(.largeTitle)
}
}
requestReview
struct ContentView: View {
@Environment(\.requestReview) var requestReview
var body: some View {
Button("Review the app") {
requestReview()
}
}
}
keyboard
struct ContentView: View {
@State private var username = "sinduke"
@State private var bio = ""
var body: some View {
ScrollView {
VStack {
TextField("Username", text: $username)
TextEditor(text: $bio)
.frame(height: 400)
.border(.quaternary, width: 1)
}
.padding(.horizontal)
}
.scrollDismissesKeyboard(.interactively)
}
}
Hiding scroll indicators
struct ContentView: View {
@State private var username = "sinduke"
@State private var bio = ""
var body: some View {
ScrollView {
VStack {
TextField("Username", text: $username)
TextEditor(text: $bio)
.frame(height: 400)
.border(.quaternary, width: 1)
}
.padding(.horizontal)
}
.scrollIndicators(.hidden)
}
}
Hiding the home and multitasking indicators
struct ContentView: View {
@State private var username = "sinduke"
@State private var bio = ""
var body: some View {
ScrollView {
VStack {
TextField("Username", text: $username)
TextEditor(text: $bio)
.frame(height: 400)
.border(.quaternary, width: 1)
}
.padding(.horizontal)
}
.persistentSystemOverlays(.hidden)
}
}
Variable values for SF Symbols
struct ContentView: View {
var body: some View {
Image(systemName: "wifi", variableValue: 0.5)
.font(.largeTitle)
}
}
struct ContentView: View {
@State private var value = 0.0
var body: some View {
VStack {
HStack {
Image(systemName: "aqi.low", variableValue: value)
Image(systemName: "wifi", variableValue: value)
Image(systemName: "chart.bar.fill", variableValue: value)
Image(systemName: "waveform", variableValue: value)
}
Slider(value: $value)
}
.font(.system(size: 62))
.foregroundColor(.blue)
.padding()
}
}
Line limit
struct ContentView: View {
var body: some View {
Text("文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本")
.lineLimit(3...6)
}
}
Text animation
struct ContentView: View {
@State private var useBlack = false
var body: some View {
Text("Hello world")
.font(.largeTitle)
.fontWeight(useBlack ? .black : .ultraLight)
.onTapGesture {
withAnimation {
useBlack.toggle()
}
}
}
}
Automatic expanding text
struct ContentView: View {
@State private var bio = ""
var body: some View {
TextField("Enter your bio", text: $bio, axis: .vertical)
.textFieldStyle(.roundedBorder)
.padding()
}
}
struct ContentView: View {
@State private var bio = ""
var body: some View {
TextField("Enter your bio", text: $bio, axis: .vertical)
.textFieldStyle(.roundedBorder)
.lineLimit(...5)
.padding()
}
}
Bold and Italic now take Booleans
struct ContentView: View {
@State private var useBold = false
@State private var useItalic = false
var body: some View {
VStack {
Text("Welcome to SwiftUI 4.0")
.bold(useBold)
.italic(useItalic)
Toggle("Use Bold", isOn: $useBold)
Toggle("Use Italic", isOn: $useItalic)
}
.font(.largeTitle)
.padding()
}
}
Foreground color animation
struct ContentView: View {
@State private var useRed = false
var body: some View {
Text("Welcome to SwiftUI")
.font(.largeTitle.bold())
.foregroundColor(useRed ? .red : .black)
.onTapGesture {
withAnimation {
useRed.toggle()
}
}
}
}
Toggling an array of Booleans at once
struct EmailList: Hashable, Identifiable {
var id: String
var isSubscribed = false
}
struct ContentView: View {
@State private var lists = [
EmailList(id: "Monthly Updates", isSubscribed: true),
EmailList(id: "NewsFlashes", isSubscribed: false),
EmailList(id: "Spacial Offers", isSubscribed: true)
]
var body: some View {
Form {
Section {
ForEach($lists, id: \.id) { $list in
Toggle(list.id, isOn: $list.isSubscribed)
}
}
Section {
Toggle(sources: $lists, isOn: \.isSubscribed) {
Text("Subscribe to all")
}
}
}
}
}