美文网首页Swift开发swiftSwift
SwiftUI教程(三)常用View和Modifiers详细讲解

SwiftUI教程(三)常用View和Modifiers详细讲解

作者: iOS之文一 | 来源:发表于2022-07-22 11:49 被阅读0次

    SwiftUI教程系列文章汇总

    本文主要讲述常见的View和Modifiers的认识和使用

    主要内容:

    1. 常用View
    2. 常用Modifiers

    1. 介绍

    SwiftUI通过View视图搭建界面,使用Modifiers修饰器来修饰视图。系统提供了大量的视图和修饰器,并且还可以让我们自定义修饰器。
    既可以手动写,也可以直接拖出到代码区或者预览区。这三种方式的结果都是一样的。

    示意图:

    view和modifiers

    2、Text

    显示一行或多行的只读文本视图,可以类似于OC中的label

    //1、Text
    Text("我是一个Text,**Markdown语法加粗了**").foregroundColor(.red)
    
    • 可以使用modifiers进行修饰视图。
    • 也可以使用Markdown语法进行修饰

    3、Label

    Label用户界面项的标准标签,包括图片和标题

    //2、Label
    VStack {
        //用户界面项的标准标签,由带有标题的图标组成。
        //labelStyle设置label样式
        Label("Lightning", systemImage: "bolt.fill")
        Label("Lightning", systemImage: "bolt.fill")
            .labelStyle(.iconOnly)
        Label("Lightning", systemImage: "bolt.fill")
            .labelStyle(.titleOnly)
        //自定义label样式
        Label("Lightning", systemImage: "bolt.fill")
    //                        .labelStyle(.makeBody(RedBorderedLabelStyle))
        
        //多label统一样式
         VStack {
             Label("Rain", systemImage: "cloud.rain")
             Label("Snow", systemImage: "snow")
             Label("Sun", systemImage: "sun.max")
         }
         .labelStyle(.iconOnly)
        
        //组合标签
        Label {
            Text("wenyi")
                .font(.body)
                .foregroundColor(.primary)
            Text("ya")
                .font(.subheadline)
                .foregroundColor(.secondary)
        } icon: {
            Circle()
                .fill(.orange)
                .frame(width: 44, height: 44, alignment: .center)
                .overlay(Text("圆")).foregroundColor(.white)
        }
    }
    

    说明:

    1. 正常情况包含了图标和标题
    2. 也可以设置labelStyle,设置为只显示图标或标题
    3. labelStyle也可以直接设置到外部视图上,可以作用到内部所有的label上
    4. 我们还可以给一个label自由组合文本和图标。
    5. 上文中自定义一个Label,包含两个文本和一个图标
    6. 还可以自定义labelStyle

    4、Button

    //Button,文字和响应
    Button {
        print("button点击响应")
    } label: {
        Text("我是按钮")
    }
    

    说明:

    1. 按钮可以添加action进行响应,进行打印。
    2. 还有设置label,label上设置图标和Text。
    3. 这里仅设置了Text。

    5、Link

    通过提供目标URL和标题来创建链接

    //Link
    Link(destination: URL(string:"https://www.baidu.com/")!) {
        Text("Link")
    }
        
    Link("View Our Terms of Service",
          destination: URL(string: "https://www.example.com/TOS.html")!)
          
    //设置OpenURLAction
    Link("Visit Our Site", destination: URL(string: "https://www.example.com")!)
        .environment(\.openURL, OpenURLAction { url in
            print("Open \(url)")
            return .handled
                        })
    

    说明:

    1. destination参数用来设置URL
    2. 还需要设置标题用来描述URL
    3. 通过设置OpenURLAction覆盖默认的URL打开的方式

    6、Image

    //直接拿Assets中图片
    Image("wy")
        .resizable()
        .aspectRatio(contentMode: .fit)
    //                UIImage.init(named: "wy.png")
                
                //加载网络图片
    //            AsyncImage.
    

    说明:

    1. 直接使用Image就可以拿到图片了,这里只能使用Assets中的图片
    2. 如果想要使用文件路径下的图片,需要使用UIImage
    3. 网络图片使用AsyncImage,注意要设置占位图片或占位文字,网络图片有可能会失败

    7、TimelineView

    根据时间表更新的视图

    //定义
    struct TimelineView<Schedule, Content> where Schedule : TimelineSchedule
    
    //TimelineView
    TimelineView(.periodic(from: Date.now, by: 1.0)) {
        context in
        Text(context.date.description).font(.title)
    }
    

    说明:

    1. 两个参数,一个是计划表,一个是执行的内容
    2. 在这里时间表是从当前时间,每1秒开始执行一次
    3. 执行的内容就是打印当前时间
    4. 因此知道个视图可以做定时器使用
    5. 可以传入三种参数
      1. .everyMinute 每分钟
      2. .periodic(from: , by: )从开始时间开始,多久开始更新
      3. .explicit(更新次数)

    8、Canvas

    绘制图形

    //Canvas
    Canvas { context, size in
        context.stroke(Path(ellipseIn: CGRect(origin: .zero, size: size)),with: .color(.blue), lineWidth: 3)
    }.frame(width: 100, height: 50, alignment: .center).border(.red,width: 2)
    

    说明:

    1. Canvas就是一个画布。我们可以对画布进行绘制丰富和动态的2D图形
    2. Canvas将GraphicsContext传递给用于执行即时模式绘图操作的闭包
    3. 通过闭包进行绘制,传入两个参数,一个是绘制的内容,一个是绘制的大小
    4. Canvas还传递一个CGSize值,您可以使用它来定制您绘制的内容

    使用画布在SwiftUI视图中绘制丰富和动态的2D图形。画布将GraphicsContext传递给用于执行即时模式绘图操作的闭包。画布还传递一个CGSize值,您可以使用它来定制您绘制的内容。例如,你可以使用上下文的stroke(_:with:lineWidth:)命令来绘制一个Path实例:

    9、TextEditor

    显示可编辑文本界面的控件。相当于UITextView

    //TextEditor
    TextEditor(text: 
        .constant("Placeholder"))
        .frame(width: 100, height: 30, alignment: .center)
    

    说明:

    1. constant可以设置默认值
    2. frame可以设置TextEditor的宽高,以及对齐方式。

    10、TextField

    文本输入框

    //TextField,预览无法操作
    TextField("首字母默认大写", text: $str).frame(width: 100, height: 56, alignment: .center)
        .textInputAutocapitalization(.never)
        //自动纠错
        .disableAutocorrection(true)
    //                    .border(.red, width: 1)
    //                    .cornerRadius(20)
        .overlay{
            RoundedRectangle(cornerRadius: 20)
                .stroke(.red, lineWidth: 10)
                .padding(-10)
        }
        .onSubmit {
            print("我点击了!")
        }
        
    ...
    .contentShape(Rectangle())//追加热区设置
            .onTapGesture {
                    print("tap")
                    //热区
                    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
            }
    

    说明:

    1. 可以添加.onSubmit来捕获提交事件
    2. 点击空白区域收起键盘
    3. 设置自动大小写的场景,比如首字母大写,全部大写,全不大写等等
    4. 自动纠错默认为false就是会自动纠错
    5. 直接设置.border设置圆角,只会设置外部的,不会设置内部的,因此需要手动绘制一个图像进行覆盖
    6. 注意“热区”,只要有内容的区域都属于热区(即使这里是空白的),热区在这里不属于空白区域
    7. 因此需要追加热区设置.contentShape(Rectangle())

    11、ColorPicker

    颜色选择器(UIKit没有)

    @State var textColor = Color.red
    //ColorPicker
    //supportsOpacity是否设置透明度
    ColorPicker("picker", selection: $textColor, supportsOpacity: false).font(.largeTitle).foregroundColor(textColor)
    

    说明:

    1. 设置Picker的标题,还有设置一个选取器selection
    2. 这里拿到的$textColor就是不断刷新的颜色
    3. supportsOpacity是设置是否设置透明度,其实还可以设置Pciker的很多东西

    12、Picker

    选择器

    @State var selectedCity = city.xian
    
    //Picker
    Picker(selection: $selectedCity, label: Text("Picker").frame(width: 50, height: 10, alignment: .center)) {
        Text("taiyuan").tag(city.taiyuan)
        Text("xian").tag(city.xian)
        Text("datong").tag(city.datong)
    }.border(.orange)
    Text("this city is : \(selectedCity.rawValue)")
    

    说明:

    1. $selectedCity就是获取的值
    2. 设置多种选择项

    13、toolbar

    //设置工具栏
    NavigationView {
        Text("Hello World!").navigationTitle("navigation")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Edit") {}
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("back") {}
                }
            }
    }
    

    说明:

    1. 设置TollbarItem项是每个按钮
    2. 有很多样式可以选择
    3. 不仅可以用于导航栏,可以用在任意的工具栏中

    14、ProgressView

    进度条视图

    //ProgressView
    //                ProgressView(value: /*@START_MENU_TOKEN@*/0.5/*@END_MENU_TOKEN@*/)
    ProgressView(value: progress, total: 10, label: {
        Text("WY")
    }, currentValueLabel: {
        Text("start")
    })
    .progressViewStyle(.circular)
    Button("加1") {
        progress += 1
    }
    ProgressView().progressViewStyle(.linear)
    
    }
    

    说明:

    1. 进度条颜色的设置.tint(.red)
    2. view的颜色的设置.background(.green)
    3. 进度条值的设置,默认是0-1,我们可以也可以设置总数
    4. 空进度条的样式,可以直接设置成ProgressView(),此时是加载图,如果需要设置空进度条,就.progressViewStyle(.linear)
    5. .progressViewStyle()就可以设置进度条的样式

    15、Slider

    滑动块

    @State private var isEditing = false
    Slider(
            value: $speed,
            in: 0...100,
            step: 5
        ) {
            Text("Speed")
        } minimumValueLabel: {
            Text("0")
        } maximumValueLabel: {
            Text("100")
        } onEditingChanged: { editing in
            isEditing = editing
        }
    Text("\(speed)")
            .foregroundColor(isEditing ? .red : .blue)
    

    说明:

    1. 滑动时动态获取到的值是value
    2. in是设置滑动范围
    3. step是可以设置整个范围多少等分(可以设置自己的精确度)
    4. onEidtingChanged是滑动时的响应操作
    5. 还可以设置最大范围和最小范围显示

    16、Togle

    切换按钮,相当于UISwitch

    Toggle(isOn: $vibrateOnRing) {
        Text("Vibrate on Ring")
    }
    Toggle("Vibrate on Ring", isOn: $vibrateOnRing)
    
    • Toggle就是切换按钮
    • 这两种写法都可以,很简单,最后拿到vibrateOnRing就可以

    17、Stepper

    当您希望用户在增加或减少值时进行粒度控制时,请使用Stepper控件

    //Stepper
    Stepper {
        //左侧文本
            Text("Value: \(value) Color: \(colors[value].description)")
        } onIncrement: {//加操作
            incrementStep()
        } onDecrement: {//减操作
            decrementStep()
        }
        .padding(5)
        .background(colors[value])//设置颜色
    //设置范围,且默认加减
    Stepper(value: $value,
                    in: range,
                    step: step) {
                Text("Current: \(value) in \(range.description) " +
                     "stepping by \(step)")
            }
                .padding(10)
    
    • 可以给Stepper设置点击的效果:
    • 默认效果就是进行数值的加减计算,此时需要设置数值的总数和步长
    • 如果自定义效果,就需要设置onIncrement和onDecrement来自定义响应

    18、Gradient

    图形渐变

    代码:

    //图形渐变
    //角渐变
    AngularGradient(gradient: Gradient(colors: [Color.red, Color.blue,.purple,.red]), center: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
        
    //椭圆
    EllipticalGradient(colors:/*@START_MENU_TOKEN@*/[Color.blue, Color.green]/*@END_MENU_TOKEN@*/, center: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, startRadiusFraction: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/, endRadiusFraction: /*@START_MENU_TOKEN@*/0.5/*@END_MENU_TOKEN@*/)
    
    //线性
    LinearGradient(gradient: /*@START_MENU_TOKEN@*/Gradient(colors: [Color.red, Color.blue])/*@END_MENU_TOKEN@*/, startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/)
        
    //辐射渐变
    RadialGradient(gradient: /*@START_MENU_TOKEN@*/Gradient(colors: [Color.red, Color.blue])/*@END_MENU_TOKEN@*/, center: .bottomLeading, startRadius: 100, endRadius: 120)
    
    图形渐变

    19、searchable

    
    //创建一个model项
    struct ItemModel: Identifiable {
        var id = UUID()
        var name: String
        var detailView: DetailView
    }
    
    //创建一个详情View
    struct DetailView: View, Identifiable {
        var id = UUID()
        var detail: String
        @State var text = ""
        var body: some View {
            VStack (alignment: .leading){
                Text(detail).font(.largeTitle).foregroundColor(.gray).bold()
                    .searchable(text: $text){
                        Text("大同").searchCompletion("大同")
                        Text("太原").searchCompletion("太原")
                        Text("太原").searchCompletion("太原")
                        Text("太原").searchCompletion("太原")
                        Text("太原").searchCompletion("太原")
                    }
                Spacer()
            }
           
        }
    }
    
    //定义一个数组
    let datas: [ItemModel] = [
        ItemModel(name: "太原", detailView: DetailView(detail: "山西省会")),
        ItemModel(name: "西安", detailView: DetailView(detail: "陕西省会")),
        ItemModel(name: "银川", detailView: DetailView(detail: "宁夏省会")),
        ItemModel(name: "西宁", detailView: DetailView(detail: "青海省会")),
        ItemModel(name: "呼和浩特", detailView: DetailView(detail: "内蒙省会")),
        ItemModel(name: "郑州", detailView: DetailView(detail: "河南省会"))
    ]
    
    //创建一个viewModel,提供了数组项
    //并且还有一个filtedItems用来过滤每一项
    class ViewModel: ObservableObject {
        
        @Published var allItems: [ItemModel] = datas
        @Published var searchedItem: String = ""
        
        var filtedItems: [ItemModel] {
            searchedItem.isEmpty ? allItems : allItems.filter({ str in
                str.name.lowercased().contains(searchedItem.lowercased())
            })
        }
    }
    
    struct ContentView: View {
        @ObservedObject var vm = ViewModel()
        var body: some View {
            NavigationView {
                List {
                    ForEach(vm.filtedItems) {item in
                        NavigationLink(item.name, destination:  item.detailView)
                    }
                }
                .navigationTitle(Text("搜索页面"))
                .searchable(text: $vm.searchedItem, prompt: "输入您想要搜索的省会名称")
            }
        }
    }
    

    说明:

    • 如果给List添加.searchable,必须配合NavigationView
    • 给Text添加搜索框,可以给搜索框添加可选值,还可以给这些值设置响应

    20、List

    列表,就是OC中的TableView,是对UITableView的包装

    List{
        ForEach(todos, id:\.name){ (todo) in
            Text("wenyi")
        }
    }
    

    说明:

    1. List里面设置每一项cell
    2. 通过ForEach进行循环设置。
    3. 每个cell显示一个文本"wenyi".

    21、TabView

    Tab的View,就是之前的tabbar

    //TabView
    TabView() {
        Text("Tab Content 1").tabItem {
            Image(systemName: "person")
            Text("Tab Label 1")
        }.tag(1).badge(Text("news"))
        
        Text("Tab Content 2").tabItem {
            Text("Tab Label 2")
        }.tag(2)
    }
    

    说明:

    1. 这里设置了两项,每项包括自己的文本内容,以及item自身的内容
    2. tabItem可以设置图片和文字。
    3. 还可以设置标记.badge
    TabView

    设置动画效果:

    TabView() {
        Text("Tab Content 1").tabItem {
            Image(systemName: "person")
            Text("Tab Label 1")
        }.tag(1).badge(Text("news"))
        
        Text("Tab Content 2").tabItem {
            Text("Tab Label 2")
        }.tag(2)
    }.tabViewStyle(.page).background(.orange)
    

    说明:

    1. item只有两个点
    2. 此时也可以取消白点(indexDispalyMode: .never)
    3. 这里的点如果想要设置图片,也是可以的

    22、OnOpenURL

    用来跳转页面,通过其他APP进行跳转到当前页面

    //OnOpenURL
    struct ContentView: View {
        @State var show = true
        @State var tabSelection = 1
        var body: some View {
            TabView(selection: $tabSelection) {
                Text("Tab Content 1").tabItem {
                    Image(systemName: "person")
                    Text("Tab Label 1")
                }.tag(1).badge(Text("news"))
                
                Text("Tab Content 2").tabItem {
                    Image(systemName: "person")
                    Text("Tab Label 2")
                }.tag(2)
            }.onOpenURL { url in
                switch url.host {
                case "tab1":
                    tabSelection = 1
                case "tab2":
                    tabSelection = 2
                default:
                    show.toggle()
                }
            }
            .sheet(isPresented: $show) {
                Text("URL参数错误")
            }
        }
    }
    

    设置scheme

    设置scheme

    结果:

    结果

    说明:

    1. 给TabView增加一个跳转连接,可以从外部应用跳转到当前页面
    2. 需要在info中设置一下scheme
    3. 如果想要删除URL type中的项,可以在Info.plist中删除,而且重新打开XCode
    4. .sheet用来弹出文本视图的弹框进行参数错误提醒

    23、interactiveDismissDisab

    禁止手势滑动关闭界面,必须加在按钮的后面

    Button("Open Sheet") {
        show.toggle()
    }
    .sheet(isPresented: $show) {
        Button("Close") {
            show.toggle()
        }
        .interactiveDismissDisabled()
    }
    

    说明:

    • 给按钮设置禁止手势滑动关闭界面。此时就只能通过返回按钮返回。不能左滑返回了。

    24、contextMenu

    文本菜单

    struct ContentView: View {
        @State var backgroundColor = Color.red
        @State var isShow = true
        var body: some View {
            Text("Hello world~").bold().font(.largeTitle).foregroundColor(.white).background(backgroundColor)
                .contextMenu (isShow ? ContextMenu{
                    Button("Red") {
                        backgroundColor = .red
                    }
                    Button("Green") {
                        backgroundColor = .green
                    }
                    Button("Blue") {
                        backgroundColor = .blue
                    }
                    Button {
                        backgroundColor = .yellow
                    } label: {
                        Label("yellow", systemImage: "scribble")
                    }
                } : nil )
        }
    }
    

    说明:

    1. 给一个view增加菜单选项
    2. 也可以设置是否显示菜单
    3. 案例中菜单选项有多个按钮,点击按钮后可以改变背景颜色。
    4. 背景颜色设置到文本视图中,所以通过点击菜单选项就可以改变文本视图的背景颜色。

    25、Menu

    菜单视图,可以嵌套

    Menu("Menu") {
        Text("Item1")
        Text("Item2")
        Text("Item3")
        Button("Red") {
            backgroundColor = .red
        }
        Button("Green") {
            backgroundColor = .green
        }
        Button("Blue"){
            backgroundColor = .blue
        }
        Button("Yellow"){
            backgroundColor = .yellow
        }
        Menu("Menu") {
            Text("Item1")
            Text("Item2")
            Text("Item3")
            Button("Red") {
                backgroundColor = .red
            }
            Menu("Menu") {
                Text("Item1")
                Text("Item2")
                Text("Item3")
                Button("Red") {
                    backgroundColor = .red
                }
            }
        }
    }
    

    说明:

    1. Menu来实现菜单视图,可以设置标题
    2. 菜单中可以放文本视图和按钮视图
    3. 并还可以再嵌套菜单视图,实现多层级菜单的选择。

    26、Form

    表单视图

    Form和List在使用和现象是没有区别的,底层也是一样的,都是对UItableView的封装,可以看做是分组的List
    用于分组数据输入的控件的容器,如在设置或检查器中。

    Form {
        Text("item1")
        Text("item2")
        Text("item3")
    }
    

    说明:

    • 案例中显示三个视图文本

    27、ScrollView

    ScrollViewReader { proxy in
        Button("gotoBottom"){
            proxy.scrollTo(90)
        }
        ScrollView(.vertical, showsIndicators: false) {
            VStack(alignment: .center, spacing: 10) {
                ForEach(0..<100) {
                    Text("cell \($0)").font(.title)
                }
                .frame(maxWidth:.infinity)
            }
        }
    }
    

    说明:

    1. 设置方向,可以选择垂直还是水平方向滑动
    2. 设置是否显示Indicators
    3. 通过代理增加按钮跳转某个cell,此时需要使用ScrollViewReader来实现。
    4. 比如这里点击按钮后,可以直接跳转到底90个cell。

    28、Alert

    //Alert
    Button("show Alert") {
        show.toggle()
    }
    .alert(isPresented: $show) {
        Alert.init(title: Text("title"), message: Text("message"), dismissButton: .cancel())
    }
        
    Button("show Dialog") {
        show.toggle()
    }.confirmationDialog("dialog", isPresented: $show) {
        Button("btn1") {}
        Button("btn2") {}
        Button("btn3") {}
    }
    }
    

    说明:

    1. 这里可以设置多种按钮类型
    2. 可以设置点击事件的响应
    3. 还可以设置按钮列表,进行选择点击

    相关文章

      网友评论

        本文标题:SwiftUI教程(三)常用View和Modifiers详细讲解

        本文链接:https://www.haomeiwen.com/subject/wzgjmrtx.html