SwiftUI知识点(四)

NavigationStack

import SwiftUI

struct NavigationStackBootcamp: View {
    
    let fruits = ["Apple", "Orange", "Banana"]
    
    ///路径数组
    @State private var stackPath: [String] = []
    
    var body: some View {
        
        NavigationStack(path: $stackPath) {
            VStack{
                Button("stackPathButton") {
                    ///一下进去三个view,然后一个一个返回
                    stackPath.append(contentsOf: [
                        "xxx ", "yyy", "zzz"
                    ])
                }
            }
            .navigationTitle("这是一个标题")
            .navigationDestination(for: Int.self) { item in
                MySecondViewScreen(index: item)
            }
            .navigationDestination(for: String.self) { item in
                Text("next view: \(item)")
            }
        }
        
        Divider()
        
        //NavigationView废弃,改用NavigationStack
        NavigationStack() {
            ScrollView{
                VStack(spacing: 40){
        
                    ForEach(fruits, id: \.self) { fruit in
                        NavigationLink(value: fruit) {
                            Text(fruit)
                        }
                    }
                    
                    ForEach(0...20, id: \.self) { item in
                        
                        ///使用这种方式,不会将下级view初始化,点击的时候才会创建
                        NavigationLink(value: item) {
                            Text("current view: \(item)")
                        }
                        
                        //使用这种方法,会将所有的下级view初始化
//                        NavigationLink {
//                            MySecondViewScreen(index: item)
//                        } label: {
//                            Text("current view: \(item)")
//                        }
                    }
                }
            }
            .navigationTitle("这是一个标题")
            .navigationDestination(for: Int.self) { item in
                MySecondViewScreen(index: item)
            }
            .navigationDestination(for: String.self) { item in
                Text("next view: \(item)")
            }
        }
    }
}

struct MySecondViewScreen: View {
    
    var index: Int
    
    init(index: Int) {
        self.index = index
        print("Init Screen: \(index)")
    }
    
    var body: some View {
        Text("next view: \(index)")
    }
}

#Preview {
    NavigationStackBootcamp()
}

在这里插入图片描述

Toolbar

import SwiftUI

struct ToolbarBootcamp: View {
    
    @State private var textFieldString: String = ""
    @State private var paths: [String] = []
    
    var body: some View {
        NavigationStack(path: $paths) {
            ZStack{
                //Color.blue.ignoresSafeArea()
                Color.white.ignoresSafeArea()
                
                ScrollView{
                    TextField("Placeholder", text: $textFieldString)
                    Text("你好~")
                        .font(.largeTitle)
                        .foregroundStyle(.white)
                    
                    ForEach(0..<50) { _ in
                        Rectangle()
                            .frame(width: 200, height: 200)
                            .cornerRadius(10)
                            .foregroundColor(.blue)
                    }
                }
                
                
            }
            .navigationTitle("导航标题")
            //原来的做法
//            .navigationBarItems(
//                leading: Image(systemName: "heart.fill"),
//                trailing: Image(systemName: "heart")
//            )
            
            //toolbar的做法
            .toolbar(content: {
                //左按钮
                ToolbarItem(placement: .topBarLeading) {
                    Image(systemName: "heart.fill")
                }
                
                //中间按钮
                ToolbarItem(placement: .principal) {
                    Image(systemName: "gear")
                }
                
                //右按钮
                ToolbarItem(placement: .topBarTrailing) {
                    HStack{
                        Image(systemName: "heart.fill")
                        Image(systemName: "heart")
                    }
                }
                
                ///在键盘上方自定义View
                ToolbarItem(placement: .keyboard) {
                    Image(systemName: "gear")
                }
                
                //底部按钮
                ToolbarItem(placement: .bottomBar) {
                    Image(systemName: "gear")
                        .background(.red)
                        .frame(maxWidth: .infinity, alignment: .leading)
                }
            })
            //可以控制顶部navigationBar的隐藏与显示
            //.toolbar(.hidden, for: ToolbarPlacement.navigationBar)
            
            
            //控制顶部navigationBar是否隐藏,如果是hidden,则蓝色框直接穿过去
            //.toolbarBackground(.hidden, for: ToolbarPlacement.navigationBar)
            
            //控制navigationBar的颜色:dark light
            //.toolbarColorScheme(.dark, for: .navigationBar)
            
            .navigationBarTitleDisplayMode(.inline)
            .toolbarTitleMenu {
                Button("按钮1"){
                    paths.append("按钮1")
                }
                
                Button("按钮2"){
                    paths.append("按钮2")
                }
            }
            
            .navigationDestination(for: String.self) { value in
                Text("value: \(value)")
            }
            
        }
    }
}

#Preview {
    ToolbarBootcamp()
}

在这里插入图片描述

ResizableSheet

import SwiftUI

struct ResizableSheetBootcamp: View {
    
    @State private var showSheet: Bool = false
    @State private var detents: PresentationDetent = .large
    
    var body: some View {
        Button("Click Me") {
            showSheet.toggle()
        }
        .sheet(isPresented: $showSheet, content: {
            MyNextView(detents: $detents)
            
                //默认是large,也就是全屏
                //medium是一半的效果
                .presentationDetents([.large, .medium])
                //hidden是隐藏上面的指示器
                .presentationDragIndicator(.hidden)
                //交互消失不可以,就是,不让消失
//                .interactiveDismissDisabled()
            
                //自定义高度,是一个比例值0-1取值
                .presentationDetents([.fraction(0.9)])
                
                //按照高度显示
                .presentationDetents([.height(100)])
            
            
                .presentationDetents([.medium, .large, .fraction(0.1)], selection: $detents)
        })
    }
}

struct MyNextView: View {
    
    @Binding var detents: PresentationDetent
    
    var body: some View {
        ZStack{
            Color.red.ignoresSafeArea()
            
            VStack{
                Text("Hello World~")
                
                Button("Medim"){
                    //这里面的值,必须在包含在Detents数组里面
                    detents = .medium
                }
                
                Button("Large"){
                    //这里面的值,必须在包含在Detents数组里面
                    detents = .large
                }
                
                Button("fraction(0.1)"){
                    //这里面的值,必须在包含在Detents数组里面
                    detents = .fraction(0.1)
                }
            }
        }
        .onDisappear(perform: {
            detents = .large
        })
    }
    
    
}

#Preview {
    ResizableSheetBootcamp()
}

在这里插入图片描述

SafeAreaInset

import SwiftUI

struct SafeAreaInsetBootcamp: View {
    var body: some View {
        NavigationStack{
            List(0..<10) { _ in
                
                Rectangle()
                    .foregroundColor(.blue)//长方形的背景颜色
                    .frame(height: 300)
                
                //list的背景颜色
                .listRowBackground(Color.green)
            }
            .navigationTitle("Safe Area Inset")
            
            //其他用法:固定在头部
            .safeAreaInset(edge: .top, alignment: .trailing) {
                Text("Hi1")
                    .frame(maxWidth: .infinity)
                    .background(.yellow)
            }
            
            //其他用法:固定在某处
            .safeAreaInset(edge: .bottom, alignment: .trailing) {
                Text("Hi2")
                    .padding()
                    .background(.yellow)
                    .clipShape(Circle())//剪成圆形
                    .padding()
            }
            
            //方法一:
//            .overlay(alignment: .bottom, content: {
//                Text("Hi")
//                    .frame(maxWidth: .infinity)
//                    .background(.yellow)
//            })
            
            //方法二:使用Safe Area Inset
            .safeAreaInset(edge: .bottom, alignment: .center) {
                Text("Hi3")
                    .frame(maxWidth: .infinity)
                    .background(.yellow)
            }
            
        }
    }
}

#Preview {
    SafeAreaInsetBootcamp()
}

在这里插入图片描述

Group

import SwiftUI

struct GroupBootcamp: View {
    var body: some View {
        VStack(spacing: 50){
            Text("Hello, World!")
            
            //使用Group,统一处理。而不需要建立Stack
            Group{
                Text("Hello, World!")
                Text("Hello, World!")
            }
            .font(.subheadline)
            .foregroundStyle(.green)
        }
        .foregroundColor(.red)
        .font(.largeTitle)
    }
    
}

#Preview {
    GroupBootcamp()
}

在这里插入图片描述

AnimationUpdate

import SwiftUI

struct AnimationUpdateBootcamp: View {
    
    @State private var animate1: Bool = false
    @State private var animate2: Bool = false
    
    var body: some View {
        ZStack{
            VStack{
                Button("Action 1") {
                    animate1.toggle()
                }
                
                Button("Action 2") {
                    animate2.toggle()
                }
                
                ZStack{
                    Rectangle()
                        .foregroundColor(.blue)
                        .frame(width: 100, height: 100)
                        .frame(maxWidth: .infinity, alignment: animate1 ? .leading : .trailing)
                        .background(.green)
                        .frame(maxHeight: .infinity, alignment: animate2 ? .top : .bottom)
                        .background(.orange)
                        
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(.red)
            }
            
        }
        //value是animate1,则只有animate1才有动画效果
        .animation(.spring, value: animate1)
    }
}

#Preview {
    AnimationUpdateBootcamp()
}

在这里插入图片描述

Menu

import SwiftUI

struct MenuBootcamp: View {
    var body: some View {
        VStack(spacing: 50){
            Menu("Click Me") {
                Button("1"){
                    
                }
                
                Button("2"){
                    
                }
                
                Button("3"){
                    
                }
                
                Button("4"){
                    
                }
            }
            
            Menu("Click Me") {
                
                ControlGroup("group"){
                    Button("1"){
                        
                    }
                    
                    Button("2"){
                        
                    }
                    
                    Button("3"){
                        
                    }
                }
                
                
                Button("4"){
                    
                }
                
                Menu("Click Me") {
                    
                    ControlGroup("group2"){
                        Button("1"){
                            
                        }
                        
                        Button("2"){
                            
                        }
                        
                        Button("3"){
                            
                        }
                    }
                }
            }
        }
        
    }
}

#Preview {
    MenuBootcamp()
}

在这里插入图片描述在这里插入图片描述

NativePopover

import SwiftUI

struct NativePopoverBootcamp: View {
    
    @State private var showPopover1: Bool = false
    @State private var showPopover2: Bool = false
    @State private var showPopover3: Bool = false
    @State private var showPopover4: Bool = false
    @State private var showPopover5: Bool = false
    
    private var array: [String] = [
        "123", "456", "789"
    ]
    
    var body: some View {
        
        ZStack{
            Color.green.ignoresSafeArea()
            
            ZStack{
                VStack(spacing: 50){
                    Button("Click Me .sheet") {
                        showPopover1.toggle()
                    }
                    
                    Button("Click Me .fullScreenCover") {
                        showPopover2.toggle()
                    }
                    
                    
                    Button("Click Me .popover") {
                        showPopover3.toggle()
                    }
                    
                    Button("Click Me .popover") {
                        showPopover4.toggle()
                    }
                    
                    Button("Click Me .popover") {
                        showPopover5.toggle()
                    }
                    .padding(.bottom, -100)
                }
                .background(.red)
                
                
                .popover(isPresented: $showPopover1, content: {
                    Text("Hello Next View")
                        .presentationCompactAdaptation(PresentationAdaptation.sheet)//出现的方式的设置
                })
                
                .popover(isPresented: $showPopover2, content: {
                    Text("Hello Next View")
                        .presentationCompactAdaptation(PresentationAdaptation.fullScreenCover)
                })
                
                
                .popover(isPresented: $showPopover3, content: {
                    Text("Hello Next View")
                        .presentationCompactAdaptation(PresentationAdaptation.popover)
                })
                
                .popover(isPresented: $showPopover4, attachmentAnchor: .point(UnitPoint.center), arrowEdge: .top, content: {
                    
                    ScrollView{
                        VStack(alignment: .leading, spacing: 12, content: {
                            ForEach(0..<array.count) { item in
                                Button(array[item]) {
                                    
                                }
                                .foregroundStyle(.black)
                                
                                if item != array.count - 1{
                                    Divider()
                                }
                            }
                        })
                        .padding(20)
                    }
                    .presentationCompactAdaptation(PresentationAdaptation.popover)
                    
                        
                })
                
                .popover(isPresented: $showPopover5, attachmentAnchor: .point(UnitPoint.bottom), arrowEdge: .top, content: {
                    Text("Hello Next View")
                        .presentationCompactAdaptation(PresentationAdaptation.popover)
                })
            }
        }
    }
}

#Preview {
    NativePopoverBootcamp()
}

在这里插入图片描述

AnyLayout

import SwiftUI

struct AnyLayoutBootcamp: View {
    
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @Environment(\.verticalSizeClass) private var verticalSizeClass
    
    var body: some View {
        VStack(spacing: 12, content: {
            Text("HorizontalSizeClass: \(horizontalSizeClass.debugDescription)")
            
            Text("VerticalSizeClass: \(verticalSizeClass.debugDescription)")
        })
        
        
        ///如果水平方向比较紧凑,那么就VStack
        if horizontalSizeClass == .compact {
            VStack{
                Text("1111111111")
                Text("2222222222")
                Text("33333333333")
            }
        }else{//如果水平方向比较能放下,那么就HStack
            HStack{
                Text("1111111111")
                Text("2222222222")
                Text("33333333333")
            }
        }
        
        //以上借助AnyLayout可以简化:
        let layout: AnyLayout = horizontalSizeClass == .compact ? AnyLayout(VStackLayout()) : AnyLayout(HStackLayout())
        layout{
            Text("1111111111")
            Text("2222222222")
            Text("33333333333")
        }
        
        
    }
}

#Preview {
    AnyLayoutBootcamp()
}

在这里插入图片描述

ViewThatFits

import SwiftUI

struct ViewThatFitsBootcamp: View {
    var body: some View {
        ZStack{
            Color.red.ignoresSafeArea()
            
            VStack{
                Text("123456789012345678901234567890")
                Divider()
                Text("123456789012345678901234567890")
                    .lineLimit(1)//限制1行
                    .minimumScaleFactor(0.1)//原来的0.1倍
                Divider()
                //按顺序找:找到最合适(可以显示全)的一个的显示
                ViewThatFits{
                    Text("123456789012345678901234567890")
                    Text("12345678901234567")
                    Text("12345678")
                }
            }
        }
        .frame(height: 300)
        .padding(20)
        .font(.largeTitle)
    }
}

#Preview {
    ViewThatFitsBootcamp()
}

在这里插入图片描述

NavigationSplitView

import SwiftUI

///NavigationSplitView主要应用在iPad
struct NavigationSplitViewBootcamp: View {
    
    @State private var visibility: NavigationSplitViewVisibility = .detailOnly
    
    var body: some View {
//        NavigationSplitView {
//            ZStack{
//                Color.red//左边的view
//                Text("123")
//            }
//            
//        } detail: {
//            ZStack{
//                Color.blue//留下的大的view
//                Text("456")
//            }
//            
//        }
//        
        
        NavigationSplitView {
            Color.red//最左边的view
        } content: {
            Color.green//中间的view(横屏的时候,绿色是显示的)
        } detail: {
            Color.blue//留下的大的view
        }
        
        
        NavigationSplitView(columnVisibility: $visibility) {
            Color.red//最左边的view
        } content: {
            Color.green//中间的view  $visibility可以控制这个 相当于自定义
        } detail: {
            ZStack(alignment: .topLeading){
                Color.blue//留下的大的view
                Text("123")
                    .padding(.leading, 10)
                    .padding(.top, 15)
            }
        }
        .navigationSplitViewStyle(.prominentDetail)//控制右边有没有黑色图层。其实是在他蓝色上面?还是在蓝色右边


    }
}

#Preview {
    NavigationSplitViewBootcamp()
}

在这里插入图片描述

GridView

import SwiftUI

struct GridViewBootcamp: View {
    var body: some View {
        ScrollView(){
            Grid(){
                
                GridRow {
                    cell(int: 1)
                    cell(int: 2)
                    cell(int: 3)
                }
                
                //神奇
    //            cell(int: 666666666)
                
                //从左到右,顶格
    //            Divider()
               
                //上面Grid多大,线多宽
                Divider()
                    .frame(height: 10)
                    .gridCellUnsizedAxes(.horizontal)
                    .background(.blue)
                
                GridRow {
                    cell(int: 4)
                    cell(int: 5)
                }
                
                
                cell(int: 6)
                cell(int: 7)
                cell(int: 8)
            }
            .background(.gray)
            
            Grid(){
                
                ForEach(0..<4) { rowIndex in
                    GridRow{
                        ForEach(0..<4) { columnIndex in
                            
                            let cellNumber = (columnIndex + 1) + (rowIndex * 4)
                            cell(int: cellNumber)
                        }
                    }
                }
            }
            .background(.purple)
            
            Grid(alignment: .trailing, horizontalSpacing: 5, verticalSpacing: 20, content: {
                ForEach(0..<4) { rowIndex in
                    GridRow{
                        ForEach(0..<4) { columnIndex in
                            
                            let cellNumber = (columnIndex + 1) + (rowIndex * 4)
                            cell(int: cellNumber)
                        }
                    }
                }
            })
            .background(.yellow)
            
            
            
            Grid(alignment: .trailing, horizontalSpacing: 5, verticalSpacing: 20, content: {
                ForEach(0..<4) { rowIndex in
                    GridRow{
                        ForEach(0..<4) { columnIndex in
                            let cellNumber = (columnIndex + 1) + (rowIndex * 4)
                            //第7个是空白
                            if cellNumber == 7 {
                                Color.clear
                                    .gridCellUnsizedAxes([.horizontal, .vertical])
                            }else{
                                cell(int: cellNumber)
                            }
                            
                            
                        }
                    }
                }
            })
            .background(.blue)
            
            
            Grid(alignment: .center, horizontalSpacing: 5, verticalSpacing: 20, content: {
                ForEach(0..<4) { rowIndex in
                    GridRow{
                        ForEach(0..<4) { columnIndex in
                            let cellNumber = (columnIndex + 1) + (rowIndex * 4)
                            //第7个是空白
                            if cellNumber == 7 {
                                EmptyView()
                            }else{
                                cell(int: cellNumber)
                                    //做到7是空的,然后6占两列
                                    .gridCellColumns(cellNumber == 6 ? 2 : 1)//一个单元格占几列,默认是1列
                                    .gridColumnAlignment(cellNumber == 6 ? .center : .trailing)
                            }
                        }
                    }
                }
            })
            .background(Color.brown)
        }
        
        
        
    }
    
    private func cell(int: Int) -> some View {
        Text("\(int)")
            .font(.largeTitle)
            .padding()
            .background(.orange)
    }
}

#Preview {
    GridViewBootcamp()
}

在这里插入图片描述

ContentUnavailableView

import SwiftUI

struct ContentUnavailableViewBootcamp: View {
    var body: some View {
        ContentUnavailableView(
            "暂无网络",
            systemImage: "wifi.slash",
            description: Text("请检查网络,刷新后再试~😄"))
        
        //更简单
        ContentUnavailableView.search
        
        
    }
}

#Preview {
    ContentUnavailableViewBootcamp()
}

在这里插入图片描述

ObservableBootcamp

iOS之前的做法:

import SwiftUI

private class ObservableViewModel: ObservableObject{
    @Published var title: String = "Some title"
}

private struct ObservableBootcamp: View {
    
    @StateObject private var viewModel = ObservableViewModel()
    
    var body: some View {
        VStack(spacing: 40){
            Button(viewModel.title){
                viewModel.title = "new title~"
            }
            .font(.largeTitle)
            
            SomeChildView(viewModel: viewModel)
            
            SomeThirdChildView()
        }
        .environmentObject(viewModel)
    }
}

private struct SomeChildView: View {
    //接收  使用`@ObservedObjec`t,调用的时候要初始化给值
    @ObservedObject var viewModel: ObservableViewModel
    var body: some View {
        Button(viewModel.title){
            viewModel.title = "subView title~"
        }
        .font(.largeTitle)
    }
}

private struct SomeThirdChildView: View {
    //接收 使用`@EnvironmentObject`t,调用的时候不需要初始化给值,但是需要手动写:`.environmentObject(viewModel)`
    @EnvironmentObject var viewModel: ObservableViewModel
    var body: some View {
        Button(viewModel.title){
            viewModel.title = "thirdChildView title~"
        }
        .font(.largeTitle)
    }
}


#Preview {
    ObservableBootcamp()
}


iOS17的做法

import SwiftUI

//1
@Observable private class ObservableViewModel{
    //2
    var title: String = "Some title"
}

private struct ObservableBootcampIOS17: View {
    //3
    @State private var viewModel = ObservableViewModel()
    
    var body: some View {
        VStack(spacing: 40){
            Button(viewModel.title){
                viewModel.title = "new title~"
            }
            .font(.largeTitle)
            
            SomeChildView(viewModel: viewModel)
            
            SomeThirdChildView()
        }
        //4
        .environment(viewModel)
    }
}

private struct SomeChildView: View {
    //接收  使用`@ObservedObjec`t,调用的时候要初始化给值
    //6
    @Bindable var viewModel: ObservableViewModel
    var body: some View {
        Button(viewModel.title){
            viewModel.title = "subView title~"
        }
        .font(.largeTitle)
    }
}

private struct SomeThirdChildView: View {
    //接收 使用`@EnvironmentObject`t,调用的时候不需要初始化给值,但是需要手动写:`.environmentObject(viewModel)`
    //5
    @Environment(ObservableViewModel.self) var viewModel
    var body: some View {
        Button(viewModel.title){
            viewModel.title = "thirdChildView title~"
        }
        .font(.largeTitle)
    }
}


#Preview {
    ObservableBootcampIOS17()
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值