Stepper
import SwiftUI
struct StepperBootcamp: View {
@State var stepperValue: Int = 10
@State var widthValue: CGFloat = 0
var body: some View {
VStack{
///步长为1
Stepper("Stepper:\(stepperValue)", value: $stepperValue)
Rectangle()
.frame(width: 100 + widthValue, height: 100)
.cornerRadius(20)
Stepper("Stepper:\(stepperValue)") {
incrementWidth(amount: 10)
} onDecrement: {
incrementWidth(amount: -10)
}
}
.padding()
}
func incrementWidth(amount: CGFloat){
withAnimation {
widthValue += amount
}
}
}
#Preview {
StepperBootcamp()
}
Slider
可以将滑块线做成随着滑动距离而现实不同的颜色,超级炫酷~
import SwiftUI
struct SliderBootcamp: View {
@State var sliderValue1: CGFloat = 0.0
@State var sliderValue2: CGFloat = 0.0
@State var sliderValue3: CGFloat = 50.0
@State var sliderValue4: CGFloat = 50.0
@State var myColor: Color = .red
var body: some View {
VStack {
Text("value: \(sliderValue1)")
Slider(value: $sliderValue1) {
Text("\(sliderValue1)")
}
Text("value: \(sliderValue2)")
//给个步长step
Slider(value: $sliderValue2, in: 0...10, step: 2)
Text("value: \(sliderValue3)")
Slider(value: $sliderValue3, in: 0...100) {
//改变的过程
Text("123")
} minimumValueLabel: {
Text("0")
} maximumValueLabel: {
Text("100")
} onEditingChanged: { (_) in
myColor = .green
myColor = .green.opacity(sliderValue3/100)
}
// 使用颜色插值从黑色到白色
Text("滑动控制颜色变化")
Color(white: sliderValue1)
.frame(width: 100, height: 100)
.cornerRadius(20)
//滑动控制颜色变化
Text("滑动控制颜色变化")
Rectangle()
.fill(Color(hue: sliderValue1, saturation: 1.0, brightness: 1.0))
.frame(width: 100, height: 100)
.cornerRadius(20)
Slider(value: $sliderValue1, in: 0...1)
.padding()
.accentColor(Color(hue: sliderValue1, saturation: 1.0, brightness: 1.0))//颜色渐变
Spacer()
//自动变化颜色
Text("自动颜色变化")
Rectangle()
.fill(Color(hue: sliderValue4, saturation: 1.0, brightness: 1.0))
.frame(width: 100, height: 100)
.cornerRadius(20)
}
.onAppear {
Timer.scheduledTimer(withTimeInterval: 0.08, repeats: true) { _ in // 你也可以调节时间间隔
withAnimation(Animation.linear(duration: 0.01)) { //确保颜色变化动画是线性的
self.sliderValue4 += 0.01
if self.sliderValue4 > 1.0 {
self.sliderValue4 = 0.0 // 重置以继续循环
}
}
}
}
}
}
#Preview {
SliderBootcamp()
}
超级炫酷~
TabView
import SwiftUI
struct TabViewBootcamp: View {
@State var selectedTabIndex: Int = 2
var body: some View {
TabView (selection: $selectedTabIndex){
HomeView(selectedTabIndex: $selectedTabIndex)
.tabItem {
Text("首页")
Image(systemName: "house.fill")
}
.tag(0)
Text("浏览内容")
.tabItem {
Text("浏览")
Image(systemName: "globe.asia.australia")
}
.tag(1)
PersonView()
.tabItem {
Text("我的")
Image(systemName: "person.fill")
}
.tag(2)
}
.tint(.orange)
}
}
#Preview {
TabViewBootcamp()
}
struct HomeView: View {
@Binding var selectedTabIndex: Int
var body: some View {
ZStack{
Color.red.ignoresSafeArea(.all, edges: .top)
VStack{
Text("首页")
.font(.largeTitle)
.foregroundStyle(.white)
Button {
self.selectedTabIndex = 2
} label: {
Text("跳转到我的页")
.foregroundStyle(.white)
.font(.title)
}
.padding()
.background(.blue)
.cornerRadius(10.0)
}
}
}
}
struct PersonView: View {
let icons: [String] = [
"heart.fill", "globe", "house.fill", "person.fill"
]
var body: some View {
//必须加VStack,不然就凭空都了一个“我的”
VStack{
Text("我的内容")
TabView {
ForEach(icons, id: \.self) { item in
Image(systemName: item)
//可调节大小的
.resizable()
.foregroundStyle(.blue).opacity(0.8)
.padding(30)
}
Rectangle()
.foregroundStyle(.red)
Rectangle()
.foregroundStyle(.green)
Rectangle()
.foregroundStyle(.yellow)
}
//分页样式
.tabViewStyle(PageTabViewStyle())
.frame(maxWidth: .infinity, maxHeight: 400)
.cornerRadius(10)
//背景色
.background(
///渐变色
///gradient:渐变的颜色
///center: 位置
///startRadius开始的长度,endRadius结束的长度
RadialGradient(gradient: Gradient(colors: [.red, .green, .yellow]), center: .topLeading, startRadius: 0.0, endRadius: 500.0)
)
}
}
}
DarkModel
深色模式适配
黑白是不适应的,即黑色在暗黑模式下黑色背景下不显示、白色在正常模式白色背景下不显示
其他颜色,比如红、黄、绿,在这两种模式下都可以显示
可以在Assets文件夹下,建立自定义颜色(两种模式下都有颜色),从而达到一种颜色值,两种不同的显示
也可以在本地做判断,利用@Environment(\.colorScheme) var colorScheme
,然后判断当前模式是.light
还是.dark
,来设置颜色
import SwiftUI
struct DarkModeBootcamp: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
NavigationView{
ScrollView{
VStack{
Text("这个颜色是Primary")
.foregroundStyle(.primary)
Text("这个颜色是secondary")
.foregroundStyle(.secondary)
Text("这个颜色是black")
.foregroundStyle(.black)
Text("这个颜色是white")
.foregroundStyle(.white)
Text("这个颜色是red")
.foregroundStyle(.red)
Text("这个颜色是yellow")
.foregroundStyle(.yellow)
Text("这个颜色是green")
.foregroundStyle(.green)
Text("这个颜色是全局的自适应颜色(globally adaptive)")
.foregroundStyle(.adaptive)
Text("本地设置颜色")
.foregroundStyle(colorScheme == .light ? .black : .white)
}
}
.navigationTitle("Dark Mode Bootcamp")
}
}
}
#Preview {
DarkModeBootcamp()
}
OnAppear
import SwiftUI
struct OnAppearBootcamp: View {
@State var textString: String = "这是一个初始的字符串"
@State var count: Int = 0
var body: some View {
NavigationView{
ScrollView{
Text(textString)
LazyVStack{
ForEach(0..<50) { _ in
Rectangle()
.cornerRadius(30.0)
.frame(height: 300)
.padding()
.onAppear(perform: {
count += 1
})
}
}
}
.onAppear(perform: {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
textString = "这是onAppear后的字符串"
}
})
.onDisappear(perform: {
//view不显示的时候调用
})
.navigationTitle("On Appear \(count)")
}
}
}
#Preview {
OnAppearBootcamp()
}
明明显示两个,count应该是2或者3,怎么是4呢???
没事,运行起来显示的就是2
TapGesture
import SwiftUI
struct TapGestureBootcamp: View {
@State var isSelected: Bool = false
var body: some View {
VStack {
Rectangle()
.foregroundStyle(isSelected ? .red : .green)
.cornerRadius(30)
.frame(height: 200)
.padding(.bottom, 20)
Button(action: {
isSelected.toggle()
}, label: {
Text("Button")
.foregroundStyle(.white)
.frame(height: 60)
.frame(maxWidth: .infinity)
.background(.blue)
.cornerRadius(5)
})
.padding(.bottom, 20)
Text("Tap Gesture")
.foregroundStyle(.white)
.frame(height: 60)
.frame(maxWidth: .infinity)
.background(.blue)
.cornerRadius(5)
//添加tapGesture
.onTapGesture {
isSelected.toggle()
}
}
.padding(50)
Spacer()
}
}
#Preview {
TapGestureBootcamp()
}
Model
import SwiftUI
struct UserModel: Identifiable {
let id: String = UUID().uuidString
let name: String
let subName: String
let isAuthentication: Bool
let followCount: Int
}
struct ModelBootcamp: View {
var userArray = [
UserModel(name: "张三", subName: "zhangsan", isAuthentication: true, followCount: 100),
UserModel(name: "李四", subName: "lisi", isAuthentication: false, followCount: 55),
UserModel(name: "王五", subName: "wangwu", isAuthentication: false, followCount: 355),
UserModel(name: "赵六", subName: "zhaoliu", isAuthentication: true, followCount: 88),
]
var body: some View {
NavigationView{
List{
ForEach(0..<userArray.count) { index in
HStack {
Circle()
.frame(width: 40, height: 40)
.foregroundColor(.yellow)
VStack{
Text(userArray[index].name)
.font(.title2)
Text("@\(userArray[index].subName)")
.font(.caption)
.foregroundStyle(.gray)
}
Spacer()
if userArray[index].isAuthentication {
Image(systemName: "checkmark.seal.fill")
.foregroundColor(.blue)
}
VStack{
Text("\(userArray[index].followCount)")
.font(.title2)
Text("Followers")
.font(.caption)
.foregroundStyle(.gray)
}
}
.padding(.vertical, 10)
}
}
.navigationTitle("Users")
}
}
}
#Preview {
ModelBootcamp()
}
ViewModel
@StateObject
监听对象的改变,并且在view刷新的时候,不会改变
在本view里面创建或者初始化
@ObservedObject
监听对象的改变,并且在view刷新的时候也会改变
传递到下一个view使用,则使用ObservedObject
import SwiftUI
struct FruitModel : Hashable, Identifiable{
let id = UUID()
let name: String
let color: String
let count: Int
}
class FruitViewModel: ObservableObject {
@Published var fruitArray:[FruitModel] = []
@Published var isLoading: Bool = false
func getFruits(){
let fruit1 = FruitModel(name: "apple", color: "green", count: 2)
let fruit2 = FruitModel(name: "banana", color: "yellow", count: 1)
let fruit3 = FruitModel(name: "grape", color: "purple", count: 100)
let fruit4 = FruitModel(name: "orange", color: "orange", count: 5)
isLoading = true
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.0) {
self.fruitArray.append(fruit1)
self.fruitArray.append(fruit2)
self.fruitArray.append(fruit3)
self.fruitArray.append(fruit4)
self.isLoading = false
}
}
func deleteFruit(index: IndexSet){
fruitArray.remove(atOffsets: index)
}
}
struct ViewModelBootcamp: View {
//监听对象的改变,并且在view刷新的时候,不会改变
//在本view里面创建或者初始化
@StateObject var fruitViewModel: FruitViewModel = FruitViewModel()
//监听对象的改变,并且在view刷新的时候也会改变
//传递到下一个view使用,则使用ObservedObject
//@ObservedObject var fruitViewModel: FruitViewModel = FruitViewModel()
var body: some View {
NavigationView{
List{
if fruitViewModel.isLoading{
ProgressView()
}else{
ForEach(fruitViewModel.fruitArray) { item in
HStack {
NavigationLink {
//下一个View的内容
SecondScreen(fruitViewModel: fruitViewModel, selectedFruitModel: item)
} label: {
Text("\(item.count)个")
.foregroundStyle(.red)
Text(item.name)
.foregroundStyle(Color(wordName: item.color) ?? .black)
}
}
.font(.title)
}
.onDelete(perform: { indexSet in
fruitViewModel.deleteFruit(index: indexSet)
})
}
}
.navigationTitle("Fruit List")
}
.onAppear{
fruitViewModel.getFruits()
}
// .onAppear(perform: {
// fruitViewModel.getFruits()
// })
}
}
struct SecondScreen: View {
@Environment(\.presentationMode) var presentationModel
//@Environment(\.isPresented) var isPresented
@ObservedObject var fruitViewModel: FruitViewModel
//不需要监听状态的改变,只是传值
var selectedFruitModel: FruitModel
var body: some View {
ZStack {
Color.blue.opacity(0.5).ignoresSafeArea()
VStack{
Text(selectedFruitModel.name)
.font(.largeTitle)
.foregroundStyle(Color(wordName: selectedFruitModel.color) ?? .white)
Button(action: {
//返回
// isPresented = true
presentationModel.wrappedValue.dismiss()
}, label: {
Text("Back")
.foregroundStyle(.black)
.font(.largeTitle)
})
}
}
}
}
#Preview {
ViewModelBootcamp()
}
Environment
import SwiftUI
class EnvironmentObjectViewModel: ObservableObject{
@Published var dataArray: [String] = []
init() {
getData()
}
func getData(){
self.dataArray.append(contentsOf: ["iPhone", "iPad", "iMac", "Apple Watch"])
}
}
struct EnvironmentObjectBootcamp: View {
@StateObject var viewMolde: EnvironmentObjectViewModel = EnvironmentObjectViewModel()
var body: some View {
NavigationView{
List{
ForEach(viewMolde.dataArray, id: \.self) { item in
NavigationLink {
SecondView(selectedItem: item)
} label: {
Text(item)
}
}
}
.navigationTitle("iOS Devices")
}
///在这写上后,后面所有的NavigationView的子view都有这个属性
.environmentObject(viewMolde)
}
}
struct SecondView: View {
var selectedItem: String
var body: some View{
ZStack{
Color.orange.ignoresSafeArea()
NavigationLink {
ThirdView()
} label: {
Text(selectedItem)
.font(.largeTitle)
.foregroundStyle(.orange)
.padding()
.background(.white)
.cornerRadius(30)
}
}
}
}
struct ThirdView: View {
//@EnvironmentObject 接收上面已经定义的属性
@EnvironmentObject var viewModel: EnvironmentObjectViewModel
var body: some View{
ZStack {
LinearGradient(gradient: Gradient(colors: [.red, .yellow, .green]),
startPoint: .leading,
endPoint: .trailing)
.ignoresSafeArea()
ScrollView{
VStack(spacing: 20){
ForEach(viewModel.dataArray, id: \.self) { item in
Text(item)
}
}
.foregroundColor(.white)
.font(.largeTitle)
}
}
}
}
#Preview {
EnvironmentObjectBootcamp()
}
AppStorage
@AppStorage("age") var currentUserAge: Int?
import SwiftUI
struct AppStorageBootcamp: View {
//方法一:使用UserDefaults.standard
@State var currentUserName: String?
//方法二:使用AppStorage
@AppStorage("age") var currentUserAge: Int?
var body: some View {
Text(currentUserName ?? "Add Name Here")
if let name = currentUserName{
Text(name)
}
Button("Save".uppercased()){
let name: String = "Nick"
currentUserName = name
currentUserAge = 100
UserDefaults.standard.setValue(name, forKey: "name")
}
.onAppear(){
currentUserName = UserDefaults.standard.value(forKey: "name") as? String
}
if let age = currentUserAge {
Text("age = \(String(describing: age))")
}
}
}
#Preview {
AppStorageBootcamp()
}
AsyncImage
import SwiftUI
struct AsyncImageBootcamp: View {
let url = URL(string: "https://picsum.photos/200")
var body: some View {
VStack{
AsyncImage(url: url)
.frame(width: 100, height: 100)
Spacer()
AsyncImage(url: url) { Image in
Image
} placeholder: {
ProgressView()
}
Spacer()
}
}
}
#Preview {
AsyncImageBootcamp()
}
BackgroundMaterials
import SwiftUI
struct BackgroundMaterialsBootcamp: View {
var body: some View {
VStack{
Spacer()
VStack{
RoundedRectangle(cornerRadius: 4)
.frame(width: 50, height: 4)
.padding()
Spacer()
}
.frame(maxWidth: .infinity)
.frame(height: 300)
.background(.thinMaterial)//设置磨砂效果
.cornerRadius(30)
}
.ignoresSafeArea()
.background(Image("dinner"))
}
}
#Preview {
BackgroundMaterialsBootcamp()
}
ButtonStyles
import SwiftUI
struct ButtonStylesBootcamp: View {
var body: some View {
VStack{
Button("Button Title") {
}
.frame(height: 55)
.frame(maxWidth: .infinity)
.buttonStyle(.plain)
Button("Button Title") {
}
.frame(height: 55)
.frame(maxWidth: .infinity)
.buttonStyle(.bordered)
Button("Button Title") {
}
.frame(height: 55)
.frame(maxWidth: .infinity)
.buttonStyle(.borderedProminent)
Button("Button Title") {
}
.frame(height: 55)
.frame(maxWidth: .infinity)
.buttonStyle(.borderless)
}
}
}
#Preview {
ButtonStylesBootcamp()
}
ListSwipeActions
import SwiftUI
struct ListSwipeActionsBootcamp: View {
@State var fruits: [String] = [
"apple", "banana", "orange", "peach"
]
var body: some View {
VStack{
List{
ForEach(fruits, id: \.self) { item in
Text(item)
///allowsFullSwipe:true允许拉很长然后触发按钮
.swipeActions(edge: .trailing, allowsFullSwipe: true){
Button("Archive"){
print("Archive")
}
.tint(.green)
Button("Save"){
print("Save")
}
.tint(.blue)
Button("Junk"){
print("Junk")
}
.tint(.black)
}
.swipeActions(edge: .leading, allowsFullSwipe: true){
Button("Share"){
print("Share")
}
.tint(.yellow)
}
}
}
}
Spacer()
}
}
#Preview {
ListSwipeActionsBootcamp()
}
Badges
import SwiftUI
struct BadgesBootcamp: View {
var body: some View {
ZStack{
Color.yellow.ignoresSafeArea(.all)
TabView{
Color.red
.tabItem {
Image(systemName: "heart.fill")
Text("Hello")
}
.badge("NEW")
Color.yellow
.tabItem {
Image(systemName: "heart.fill")
Text("Hello")
}
.badge(10)
Color.green
.tabItem {
Image(systemName: "heart.fill")
Text("Hello")
}
.badge(120)
}
}
}
}
#Preview {
BadgesBootcamp()
}