Tutorials ⟩ Views
ContentView
ContentView.swift (12)
import SwiftUI
struct ContentView: View {
/// ⭐️ 1. an enumeration of the tabs to display.
enum Tab {
case featured
case list
}
/// ⭐️ 2. state variable for the tab selection
@State private var selection: Tab = .featured
var body: some View {
/// ⭐️ 3. TabView
TabView(selection: $selection) {
/// ⭐️ 4. set tab item & tag for each view
CategoryHome().tabItem{
Label("Featured", systemImage: "star")
}.tag(Tab.featured)
LandmarkList().tabItem{
Label("List", systemImage: "list.bullet")
}.tag(Tab.list)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(ModelData())
}
}
swProfile
ProfileHost.swift (13)
ProfileEditor.swift (14)
ProfileSummary.swift (15)
import SwiftUI
/// host both a static, summary view of profile information
/// and an edit mode.
struct ProfileHost: View {
/// ⭐️ 1. copy data
/// To avoid updating the global app state before confirming any edits,
/// the editing view operates on a **copy** of the user's profile.
@State private var draftProfile = Profile.default
/// ⭐️ 2. access edit mode:
/// an `@Environment` view property that keys off of the environment’s `\.editMode`.
/// - key off: to take something as a controlling input datum. (意思跟「控制...」差不多)
/// See [FreeDictionary][1]
///
/// Storing the edit mode in the environment makes it simple for **multiple views**
/// to update when the user enters and exits edit mode.
///
/// SwiftUI provides storage in the environment for values you can access
/// using the `@Environment` property wrapper. Access the `editMode` value
/// to read or write the edit scope.
///
/// [1]: https://idioms.thefreedictionary.com/key+off
@Environment(\.editMode) var editMode
/// ⭐️ global app state
/// Read the user’s profile data from the environment
/// to pass control of the data to the profile host.
@EnvironmentObject var modelData: ModelData
var body: some View {
VStack(alignment: .leading, spacing: 20) {
HStack {
/// ⭐️ 3. cancel button
if editMode?.wrappedValue == .active {
Button("Cancel", role: .cancel) {
draftProfile = modelData.profile
editMode?.animation().wrappedValue = .inactive
}
}
Spacer()
/// ⭐️ 4. edit button
/// button that toggles the environment’s `editMode` value on and off.
EditButton()
}
/// ⭐️ 5. normal/editor view
/// displays either the static profile or
/// the view for Edit mode.
if editMode?.wrappedValue == .inactive {
ProfileSummary(profile: modelData.profile)
} else {
ProfileEditor(profile: $draftProfile)
/// ⭐️ 6. a draft copy of real data
.onAppear { draftProfile = modelData.profile }
/// ⭐️ 7. write draft data back to real data
.onDisappear { modelData.profile = draftProfile }
}
}
.padding()
}
}
struct ProfileHost_Previews: PreviewProvider {
static var previews: some View {
ProfileHost()
/// Even though this view doesn’t use a `@EnvironmentObject`,
/// `ProfileSummary`, a child of this view, does. So without it,
/// the preview fails.
.environmentObject(ModelData())
}
}
Category
CategoryHome.swift (16)
CategoryRow.swift (17)
CategoryItem.swift (18)
import SwiftUI
struct CategoryHome: View {
@EnvironmentObject var modelData: ModelData
/// ⭐️ 1. view state to control the presentation of the `ProfileHost` view
@State private var showingProfile = false
var body: some View {
NavigationView {
List {
/// featured landmark
modelData.features[0].image
.resizable()
.scaledToFill()
.frame(height: 150)
.cornerRadius(4)
/// set edge insets to zero so the content
/// can extend to the edges of the display.
.listRowInsets(EdgeInsets())
// .clipped()
/// categories
ForEach(modelData.categories.keys.sorted(), id: \.self) { key in
CategoryRow(categoryName: key, items: modelData.categories[key]!)
}.listRowInsets(EdgeInsets())
}
.listStyle(.inset)
.navigationTitle("Featured")
/// ⭐️ 2. add a button to the navigation bar
.toolbar {
Button { showingProfile.toggle() } label: {
Label("User Profile", systemImage: "person.crop.circle")
}
}
/// ⭐️ 3. present the `ProfileHost` view when the user taps the button.
.sheet(isPresented: $showingProfile) {
ProfileHost().environmentObject(modelData)
}
}
}
}
struct CategoryHome_Previews: PreviewProvider {
static var previews: some View {
CategoryHome()
.environmentObject(ModelData())
}
}
Hikes
GraphCapsule.swift (19)
HikeGraph.swift (20)
HikeDetail.swift (21)
HikeView.swift (22)
HikeBadge.swift (23)
import SwiftUI
struct GraphCapsule: View, Equatable {
var index: Int
var color: Color
var height: CGFloat
var range: Range<Double>
var overallRange: Range<Double>
var heightRatio: CGFloat {
max(CGFloat(magnitude(of: range) / magnitude(of: overallRange)), 0.15)
}
var offsetRatio: CGFloat {
CGFloat((range.lowerBound - overallRange.lowerBound) / magnitude(of: overallRange))
}
var body: some View {
Capsule()
.fill(color)
.frame(height: height * heightRatio)
.offset(x: 0, y: height * -offsetRatio)
}
}
struct GraphCapsule_Previews: PreviewProvider {
static var previews: some View {
GraphCapsule(
index: 0,
color: .blue,
height: 150,
range: 10..<50,
overallRange: 0..<100)
}
}
Badge
HexagonParameters.swift (24)
BadgeBackground.swift (25)
BadgeSymbol.swift (26)
RotatedBadgeSymbol.swift (27)
Badge.swift (28)
Parts
CircleImage.swift (29)
MapView.swift (30)
FavoriteButton.swift (31)
Landmarks
LandmarkList.swift (32)
LandmarkRow.swift (33)
LandmarkDetail.swift (34)
Last updated
Was this helpful?