
设置页面

代码:
import SwiftUIclass Settings: ObservableObject {enum AccountBehavior: CaseIterable {case register, login}enum Sorting: CaseIterable {case id, name, color, favorite}@Published var accountBehavior = AccountBehavior.login@Published var email = ""@Published var password = ""@Published var verifyPassword = ""@Published var showEnglishName = true@Published var sorting = Sorting.id@Published var showFavoriteOnly = false}//Sorting 的辅助属性extension Settings.Sorting {var text: String {switch self {case .id: return "ID"case .name: return "名字"case .color: return "颜色"case .favorite: return "最爱"}}}//AccountBehavior 的辅助属性extension Settings.AccountBehavior {var text: String {switch self {case .register: return "注册"case .login: return "登录"}}}struct SettingView: View {@ObservedObject var settings = Settings()var body: some View {Form(content: {accountSectionoptionSectionactionSection})}///账户组var accountSection: some View {Section(header: Text("账户")) {Picker(selection: $settings.accountBehavior, label: Text(""), content: {ForEach(Settings.AccountBehavior.allCases, id: \.self) {Text($0.text)}}).pickerStyle(SegmentedPickerStyle())TextField("电子邮箱",text: $settings.email)TextField("密码",text: $settings.password)if settings.accountBehavior == .register {SecureField("确认密码",text: $settings.verifyPassword)}Button(settings.accountBehavior.text) {print("登录/注册")}}}///可选组var optionSection: some View {Section(header: Text("选项")) {Toggle(isOn: $settings.showEnglishName, label: {Text("显示应为名")})Picker(selection: $settings.sorting, label: Text("排序方式"), content: {ForEach(Settings.Sorting.allCases, id: \.self) {Text($0.text)}})Toggle(isOn: $settings.showFavoriteOnly, label: {Text("只显示收藏")})}}///清除缓存var actionSection: some View {Section {Button(action: {print("清空缓存")}, label: {Text("清空缓存").foregroundColor(.red)})}}}struct SettingView_Previews: PreviewProvider {static var previews: some View {SettingView()}}
列表页面:

import SwiftUI
struct PokemonList: View {
@State var expandingIndex: Int?
var body: some View {
ScrollView(content: {
ForEach(PokemonViewModel.all) { pokemon in
PokemonlnfoRow(model: pokemon,
expanded: self.expandingIndex == pokemon.id)
.onTapGesture {
print("weiweiweiweiwei")
withAnimation(
.spring(
response: 0.55,
dampingFraction: 0.425,
blendDuration: 0
)
) {
if self.expandingIndex == pokemon.id {
self.expandingIndex = nil
}else {
self.expandingIndex = pokemon.id
}
}
}
}
})
}
}
struct PokemonList_Previews: PreviewProvider {
static var previews: some View {
PokemonList()
}
}
import SwiftUI
struct PokemonlnfoRow: View {
let model: PokemonViewModel
let expanded: Bool
var body: some View {
//
VStack {
HStack {
Image("Pokemon-\(model.id)")
.resizable()
.frame(width: 50, height: 50)
.aspectRatio(contentMode: .fit)
.shadow(radius: 4)
Spacer()
VStack(alignment: .trailing, content: {
Text(model.name)
.font(.title)
.fontWeight(.black)
.foregroundColor(.white)
Text(model.nameEN)
.font(.subheadline)
.foregroundColor(.white)
})
}
.padding(.top, 12)
Spacer()
HStack(spacing: expanded ? 20 : -30 , content: {
Spacer()
Button(action: {
print("fav")
}, label: {
Image(systemName: "star")
.modifier(ToolButtonModifier())
})
Button(action: {
print("panel")
}, label: {
Image(systemName: "chart.bar")
.modifier(ToolButtonModifier())
})
Button(action: {
print("web")
}, label: {
Image(systemName: "info.circle")
.modifier(ToolButtonModifier())
})
})
.padding(.bottom, 12)
.opacity(expanded ? 1.0 : 0.0)
.frame(maxHeight: expanded ? .infinity : 0)
}
.frame(height:expanded ? 120 : 80)
//左右边距
.padding(.leading,23)
.padding(.trailing,15)
.background (
ZStack(content: {
//加边框
RoundedRectangle(cornerRadius: 20)
.stroke(model.color, style: StrokeStyle(lineWidth: 4))
//添加渐变色
RoundedRectangle(cornerRadius: 20)
.fill(LinearGradient(
gradient: Gradient(colors: [.white,model.color]),
startPoint: .leading,
endPoint: .trailing
))
})
)
.padding(.horizontal)
}
}
struct PokemonlnfoRow_Previews: PreviewProvider {
static var previews: some View {
VStack(content: {
PokemonlnfoRow(model: .sample(id: 1), expanded: false)
PokemonlnfoRow(model: .sample(id: 21), expanded: true)
PokemonlnfoRow(model: .sample(id: 25), expanded: false)
})
}
}
struct ToolButtonModifier: ViewModifier {
func body(content: Content) -> some View {
content
.font(.system(size: 25))
.foregroundColor(.white)
.frame(width: 30, height: 30)
}
}
弹出框

import SwiftUI
extension PokemonInfoPanel {
struct Header: View {
let model: PokemonViewModel
var body: some View {
HStack(spacing: 18, content: {
pokemonIcon
nameSpecies
verticalDivider
VStack(spacing: 12, content: {
bodyStatus
typeInfo
})
})
}
///前面的icon
var pokemonIcon: some View {
Image("Pokemon-\(model.id)")
.resizable()
.frame(width: 68, height: 68)
}
///名字
var nameSpecies: some View {
VStack(spacing: 10, content: {
VStack(content: {
Text(model.name)
.font(.system(size: 22))
.fontWeight(.bold)
.foregroundColor(model.color)
Text(model.nameEN)
.font(.system(size: 13))
.fontWeight(.bold)
.foregroundColor(model.color)
})
Text(model.genus)
.font(.system(size: 13))
.fontWeight(.bold)
.foregroundColor(.gray)
})
}
///垂直分割线
var verticalDivider: some View {
RoundedRectangle(cornerRadius: 1)
.frame(width: 1, height: 44)
.opacity(0.1)
}
var bodyStatus: some View {
VStack(alignment: .leading, content: {
HStack(content: {
Text("身高")
.font(.system(size: 11))
.foregroundColor(.gray)
Text(model.height)
.font(.system(size: 11))
.foregroundColor(model.color)
})
HStack(content: {
Text("体重")
.font(.system(size: 11))
.foregroundColor(model.color)
Text(model.weight)
.font(.system(size: 11))
.foregroundColor(model.color)
})
})
}
var typeInfo: some View {
HStack(content: {
ForEach(self.model.types) { t in
ZStack {
RoundedRectangle(cornerRadius: 7)
.fill(t.color)
.frame(width: 36, height: 14)
Text(t.name)
.font(.system(size: 10))
.foregroundColor(.white)
}
}
})
}
}
}
struct PokemonInfoPanelHeader_Previews: PreviewProvider {
static var previews: some View {
PokemonInfoPanel.Header(model: .sample(id: 1))
}
}
import SwiftUI
///技能列表
extension PokemonInfoPanel {
struct AbilityList: View {
let model: PokemonViewModel
let abilityModels: [AbilityViewModel]?
var body: some View {
VStack(alignment: .leading, spacing: 12, content: {
Text("技能")
.font(.headline)
.fontWeight(.bold)
if abilityModels != nil {
ForEach(abilityModels!) { ability in
Text(ability.name)
.font(.subheadline)
.foregroundColor(self.model.color)
Text(ability.descriptionText)
.font(.footnote)
.foregroundColor(Color(hex: 0xAAAAA))
.fixedSize(horizontal: false, vertical: true)
}
}
})
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
struct PokemonInfoPanelAbilityList_Previews: PreviewProvider {
static var previews: some View {
PokemonInfoPanel(model: .sample(id: 1))
}
}
import SwiftUI
//面板信息
struct PokemonInfoPanel: View {
let model: PokemonViewModel
var abilities: [AbilityViewModel] {
AbilityViewModel.sample(pokemonID: model.id)
}
var topIndicator: some View {
RoundedRectangle(cornerRadius: 3)
.frame(width: 40, height: 6)
.opacity(0.2)
}
var pokemonDescription: some View {
Text(model.descriptionText)
.font(.callout)
.foregroundColor(Color(hex: 0x666666))
.fixedSize(horizontal: false, vertical: true)
}
var body: some View {
VStack(spacing: 20, content: {
topIndicator
Header(model: model)
pokemonDescription
Divider()
AbilityList(
model: model,
abilityModels: abilities)
})
.padding(
EdgeInsets(
top: 12,
leading: 30,
bottom: 30,
trailing: 30)
)
.blurBackground(style: .systemMaterial)
//.background(Color.white)
.cornerRadius(20)
.fixedSize(horizontal: false, vertical: true)
}
}
struct PokemonInfoPanel_Previews: PreviewProvider {
static var previews: some View {
PokemonInfoPanel(model: .sample(id: 1))
}
}
