SwiftUI 100days笔记 (16-25day)

132 阅读3分钟

16day 开始正式进入 ui 的部分

import SwiftUI

struct ContentView: View {
    @State private var checkAmount = 0.0 //monitoring changes and refresh view
    @State private var numberOfPeople = 2
    @State private var tipPercetage = 20
    //@FocusState

    let tipPercetages = [10,15,20,25,0]
    var totalAmount: Double{ //计算属性
        let checkAmount = Double(checkAmount)
        let tipPercentage = Double(tipPercetage)
        return checkAmount * (1 + tipPercentage/100)
    }
    var perPayAmount:Double{
        let peopleCount = Double(numberOfPeople + 2)
        return totalAmount/peopleCount
    }
    var body: some View { //some就类似于 c++的 auto, 表示自动推断适合的view协议 //body 是必须的

            NavigationStack{ //availbe to show new view with navigation link ,为了添加整个页面的标题,一般需要一个NavigationStack
                Form{ //form 会自动将页面设成适合填写的布局
                    Section{ //分块,空白比较大
                        TextField("Amount",value:$checkAmount,format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
                            .keyboardType(.decimalPad) //填入区 ,第一个一般是 label, $checkAmount表示binding,元变量会被修改更新
                        Picker("Number of People",selection: $numberOfPeople){ //显示多个选项,一般跟一个闭包来具体编写有几个选项,每个表示什么,一般用 text
                            ForEach(2..<100){ //here the number is only for render, not the value of numberOfPeople, so numberOfPeople should add 2 to fit the rendered number
                                Text("\($0) people")
                            }
                        }
                        .pickerStyle(.navigationLink) //show new view //这个修饰符会将选择迁移到新的画面
                    }
                    Section("Tips"){//section 可加入标题
                        Picker("Tip percentage", selection: $tipPercetage){
                            ForEach(tipPercetages,id:\.self){ //同个自身作为索引的 id 
                                Text($0,format: .percent)
                            }
                        }
                        .pickerStyle(.segmented) //不是下拉菜单,是横向的选项式
                        Picker("Precious setting",selection: $tipPercetage){
                            ForEach(0..<101){
                                Text($0,format: .percent) //format 修改显示
                            }
                        }
                        .pickerStyle(.navigationLink)
                    }
                    Section("Total Amount"){
                        Text(totalAmount,format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
                            .foregroundColor(tipPercetage == 0 ? .red:.black) //通过三元符来控制变动参数
                    }
                    Section("Amount Per Person"){
                        Text(perPayAmount,format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
                    }
                }
                .navigationTitle("WeSplit") //标题
                
            }
            
            //.navigationTitle("WeSplit") have to attach inside the stack
        
    }
}

#Preview { //在 xcode 上需要预览的画面的函数
    ContentView()
}




import SwiftUI

struct ContentView: View {
    @State private var unit = ["meter","kilometer","feet","yard","miles"]
    @State private var selectedInputUnit = "meter"
    @State private var selectedOutputUnit = "meter"
    @State private var rawNumber: Double = 0.0
    
    private var covertedToMeter: Double{
        switch selectedInputUnit {
        case "meter":
            return rawNumber
        case "kilometer":
            return rawNumber * 1000
        case "feet":
            return rawNumber * 0.3048
        case "yard":
            return rawNumber * 0.9144
        case "miles":
            return rawNumber * 1609.34
        default:
            return rawNumber
        }
    }
    
    private var covertedToOutputUnit: Double{
        switch selectedOutputUnit {
        case "meter":
            return covertedToMeter
        case "kilometer":
            return covertedToMeter * 0.001
        case "feet":
            return covertedToMeter * 3.28084
        case "yard":
            return covertedToMeter * 1.09361
        case "miles":
            return covertedToMeter * 0.000621371
        default:
            return covertedToMeter
        }
    }
    var body: some View {
        NavigationStack{
            Form{
                Section("unit setting"){
                    HStack{ //横向排列悬殊
                        Text("Input unit   ")
                        Picker("Input unit",selection: $selectedInputUnit){
                            ForEach(unit,id:\.self){
                                Text("\($0)")
                            }
                        }
                        .pickerStyle(.segmented)
                    }
                    HStack{
                        Text("Output unit")
                        
                        Picker("Output unit",selection: $selectedOutputUnit){
                            ForEach(unit,id:\.self){
                                Text("\($0)")
                            }
                        }
                        .pickerStyle(.segmented)
                    }
                }
                Section("input number"){
                    HStack {
                        TextField("Enter value", value: $rawNumber, format: .number)//format定义一定是数字
                            .keyboardType(.decimalPad) //可定义弹出的键盘类型
                        Text(selectedInputUnit)
                    }
                }
                Section("Oput number"){
                    HStack{
                        Text("\(covertedToOutputUnit.formatted())") //format ted 自动转换为 string,一般用来表示文字
                        Spacer() //分割两个元素
                        Text(" \(selectedOutputUnit)")
                    }
                    
                }
            }
            .navigationTitle("Length Conversion")
        }
        
    }
}

#Preview {
    ContentView()
}






import SwiftUI

struct clipshadowImageModifier: ViewModifier { //自定义 view 的修饰符,这里是将两个修饰符的功能合并
    func body(content: Content)-> some View{ //content 表示本来的 view
        content                                
            .clipShape(.capsule) 
            .shadow(radius: 5)
    }
}
extension View{ //扩展 View 以便方便使用该自定义修饰符
    func clipandshadow() -> some View{
        modifier(clipshadowImageModifier())
    }
}
func flagImage(_ name:String) ->  some View{ //自定义函数
    Image(name)
        .clipandshadow()
}
struct BigBlueTitleFontModifier: ViewModifier{
    func body(content: Content) -> some View {
        content
            .font(.largeTitle.weight(.bold))
            .foregroundColor(.blue)
    }
}
extension View{
    func bigbluetitle() -> some View{
        modifier(BigBlueTitleFontModifier())
    }
}

struct ContentView: View {
    @State private var countries = ["Estonia","France","Germany","Ireland","Italy","Nigeria","Poland","Spain","UK","Ukraine","US"].shuffled() //random it 自动随机顺序
    @State private var correctAnswer = Int.random(in: 0...2)
    @State private var showingScore = false
    @State private var scoreTitle = ""
    @State private var score = 0
    @State private var turn = 0
    @State private var final = false

    var body: some View {
        
        ZStack{ //上下层结构,一般用来先头放置颜色 view 作为背景
            //            LinearGradient(colors: [.blue,.black], startPoint: .top, endPoint: .bottom)
            //                .ignoresSafeArea()
            RadialGradient(stops:[.init(color: Color(red:0.1,green: 0.2,blue: 0.45), location: 0.3),.init(color: .red, location: 0.3)],center: .top, startRadius: 100, endRadius: 700) //没有定义的地方 swift 会自动设置为与最近的一个设置相同的参数,比如 0.。100 会被设置为 100 的颜色
                .ignoresSafeArea() //包括安全区也覆盖颜色
            VStack{
                Spacer()

                Text("Guess the flag")
                    .bigbluetitle() //设置字体类型
                
                VStack(spacing:30){  //设定元素之间的间距
                    VStack{
                        Text("Tap the flag of")
                            .foregroundStyle(.secondary)
                            .font(.subheadline.weight(.heavy))//weight是字体的主席
                        Text(countries[correctAnswer])
                            .font(.largeTitle.weight(.semibold))
                        Text("\(turn) turn")
                            .foregroundStyle(.secondary)
                            .font(.subheadline.weight(.heavy))
                    }
                    ForEach(0..<3){ number in //ForEach(0...3) 报错是因为范围包含的类型必须是 Range,而不是 ClosedRange。用 0..<4 来表示从0到3。
                        Button{
                            flagTapped(number)
                        }label:{ //可自定义 lable,一般是文字,图像等
                            flagImage(countries[number])
                        }
                    }
                }
                .frame(maxWidth: .infinity) //设置大小,一般可设置 maxmin 长宽
                .padding(.vertical,20) //设置周围余白 这里只设置垂直方向
                .background(.regularMaterial) //背景为模糊材料效果
                .clipShape(.rect(cornerRadius: 20)) //圆角类型
                
                Spacer() //used to fix layout with ratio of spacer number
                Spacer() //多个以控制各个空白的比例

                
                Text("Score: \(score)")
                    .foregroundStyle(.white) //颜色
                    .font(.subheadline.weight(.heavy))
                Spacer()

            }
            .padding()
            
        }
        .alert(scoreTitle,isPresented: $showingScore){ //设置警告框,isPresented需设定一个state 值,当值发生变化时会触发警告框
            Button("Continue",action: askQuestion) //设置警告框的按钮
        }message:{ //设置小字
            Text("Your score is \(score)")
        }
        .alert(scoreTitle,isPresented: $final){
            Button("Reset",action: resetScore)
        }
        message:{
            Text("Your total score is \(score)")
        }
        
            
    }
    func flagTapped(_ number: Int)
    {

        if (number == correctAnswer){
            scoreTitle = "Correct"
            score += 1
        }
        else{
            scoreTitle = "Wrong, it is \(countries[number])"
        }
        
        turn += 1
        if turn == 3{
            scoreTitle = "Game Over!"
            final = true
            return
        }
        
        showingScore = true
        

    }
    func askQuestion(){
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
    }
    func resetScore(){
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
        score = 0
        turn = 0

    }
}

#Preview {
    ContentView()
}

下面包括了一个机械学习的模型,以预测输出

还有一些日期的用法

import SwiftUI
import CoreML

struct ml: View {
    @State private var sleepAmount = 9.0
    @State private var wakeUP = defaultWakeTime
    @State private var coffeeAmount = 0
    
    @State private var alertTitle = ""
    @State private var alertMessage = ""
    @State private var showingAlert = false
    
//因为defaultWakeTime被 state 使用,所以要设置为 static 保证事先赋值好
//    var defaultWakeTime : Date{  //swift cant order the two var here, so failed compile, instead use static to priority the order here
//        var components = DateComponents()
//        components.hour = 7
//        components.minute = 0
//        return Calendar.current.date(from:components) ?? .now
//    }
    static var defaultWakeTime : Date{ 
        var components = DateComponents() //时间组件
        components.hour = 7 //定义时间
        components.minute = 0
        return Calendar.current.date(from:components) ?? .now //通过Calendar.current.date(from: 来 cast 到 Date 类型,才能被 view 使用
    }
    var body: some View {
        NavigationView{
            Form{
            //Vstack{ Form list them automatically  and looks good
               
                VStack(alignment:.leading,spacing:0){ //section divied here by vstack //aligment 对齐方式,这里是对齐左边
                //Section{ //it takes more space thran vstack section
                    Text("When do you want to wake up ?")
                        .font(.headline)
                    DatePicker("Please enter a time ", selection: $wakeUP,displayedComponents: .hourAndMinute)
                        .labelsHidden() //关闭标签显示
                }
                VStack(alignment:.leading,spacing:0){
                    Text("Desired amount of sleep")
                        .font(.headline)
                    Stepper("\(sleepAmount.formatted()) hours", value:$sleepAmount, in:4...12, step: 0.25) //选步进选择起
                }
                VStack(alignment:.leading,spacing:0){
                    Text("Daliy coffee intake")
                        .font(.headline)
                    Stepper(coffeeAmount == 1 ? "\(coffeeAmount) cup" : "\(coffeeAmount) cups",value: $coffeeAmount, in:0...20) //sovle the cup and cups
                    Stepper("^[\(coffeeAmount) cup](inflect: true)",value: $coffeeAmount, in:0...20) //sovle the cup and cups by markdown
                    Picker("Input cup", selection: $coffeeAmount){
                        ForEach(0..<11){
                            Text($0 == 1 ? "\($0) cup" : "\($0) cups")
                        }
                    }
                    
                }
            }
            .navigationTitle("BetterRest")
            .toolbar{ //顶部右边的位置
                Button("Caluculate",action:calculateBedtime) 
                Button("Test"){} //clousure buttom returns nothing //按钮可以通过空包放置没有作用的空按钮
                //Button("Test2") //not allowed
            }
            .alert(alertTitle,isPresented: $showingAlert){
                Button("OK"){}//按钮可以通过空包放置没有作用的空按钮,但这里可以直接退出警告框
            }message:{
                Text(alertMessage)
            }
            
        }

    }

    func calculateBedtime(){
        do {
//一般来说用 model 都加上 try 防止错误运行出现不可预料结果
            let config = MLModelConfiguration() //设置 MLMODEL
            let model = try SleepCalculator(configuration: config) // model instance ,通过 try 来解包,异常就终止 如果用 try?来解包可以忽略错误并返回 nil(但是在 do 里面不能用 try?)
            //current 是表示使用当前系统的日历的规格,dateComponents来设置具体取些什么,具体的值从 from 取,Date 烈性
            let components  = Calendar.current.dateComponents( [.hour,.minute], from: wakeUP)
            let hour = (components.hour ?? 0) * 60 * 60 // convert to second //需要解包
            let minute = (components.minute ?? 0 ) * 60 // convert to second //需要解包
            
            //通过prediction来输入输出模型
            let prediction = try model.prediction(wake: Double(hour+minute), estimatedSleep: sleepAmount, coffee: Double(coffeeAmount))
            
            //日期相减得到一个时间间隔,但依旧是 Date 类型
            let sleepTime = wakeUP - prediction.actualSleep 
            alertMessage = "your ideal betime is "
            //要打印 Date 类型,需要 formatted 转换为时间,可以设置 omiited 表示不输出,。shorten 为短格式
            alertMessage = sleepTime.formatted(date:.omitted,time: .shortened) //convert to string
        }
        catch
        {
            alertTitle = "Error"
            alertMessage = "Problem in calcualtiing"
            
        }
        showingAlert = true
        
    }
}



#Preview {
    ml()
}




import SwiftUI

struct ContentView: View {
    @State private var sleepAmount = 9.0
    @State private var wakeUP = Date.now //当前日期
    var body: some View {
        Stepper("\(sleepAmount.formatted()) hours", value:$sleepAmount, in:4...12, step: 0.25)//formatted 在这里去掉多余的小数位
        DatePicker("Please enter a date ", selection: $wakeUP)//选择日期的 picker , 初始值为 selection
        DatePicker("Please enter a date ", selection: $wakeUP,displayedComponents: .hourAndMinute) //displayedComponents选择那些时间
            .labelsHidden()
        DatePicker("Please enter a date ", selection: $wakeUP)
        DatePicker("Please enter a date ", selection: $wakeUP, in:Date.now...) //单向闭包是可以的
        
        Text(Date.now, format: .dateTime.hour().minute())//format 控制时间格式,后面加.来连续设置
      Text(Date.now, format: .dateTime.day().month().year())
        Text(Date.now.formatted(date: .long, time: .shortened))//另一种表示格式,用 formatted 转换为 string


    }
    func exampleDates(){
        let now = Date.now
        let tomorrow = Date.now.addingTimeInterval(86400) //增加时间,秒单位
        let range = now...tomorrow //时间的区间
        
        var components = DateComponents() //时间组件
        components.hour = 8 //设定时间
        components.minute = 0
        let date = Calendar.current.date(from: components) //将时间组件转换为 Date 类型
        
        let date2 = Calendar.current.date(from: components) ?? .now 
        
        let components2 = Calendar.current.dateComponents([.hour, .minute], from: .now)
        let hour = components2.hour ?? 0 //取出某一个单位
        let minute = components2.minute ?? 0



    }
}

#Preview {
    ContentView()
}