swiftUI List TabView 各种布局 疑难杂症 使用大全 真实项目

swiftUI 2.0了 可以尝试在自己项目中写小页面了。 注: 本文使用的是2.0 所以你的iOS版本得是14.0+ (本人从swift2.0用到现在的5.0 真的改的东西太多了,不推荐用UI1.0)

另外推荐一个网站 不知道的问题可以去直接提 SwiftUI官方论坛 点我起飞

官方对SwiftUI的介绍 基本上你想要的都有,没有的,也别搜了,教程估计就是UIKit包装一下用而已 点我起飞

不要再去某站找那些付费知识了,我就想不通 又不是商业项目 也不是你自己的开源库 就一些基础知识 也好意思收费? 我还看到一个hxd 付费看了,结果评论说 没有用,作者回复(1.0是有用的,你把版本改一下)
我?
这是这么神奇操作!!!
言归正传

本文分为 在UIKit中使用SwiftUI使用SwiftUI搭建一个app

UIKit中使用SwiftUI

目前来说 你直接从头到尾把项目改成swiftUI 有点扯淡。 在当前项目中引用UI模块比较靠谱。 而且在真实项目中才能发现问题,找到问题,解决问题。
写静态数据,恕我直言 帮助不大。

点击 跳转SWIFTUI的VC

首先在有点击事件的页面中导入UI库

import UIKit
import SwiftUI

找到点击事件 判断版本来做跳转

if #available(iOS 14.0.0, *) {
  let swiftUIvc = UIHostingController(rootView: AEMemberUserAdmin())
  controller?.navigationController?.pushViewController(swiftUIvc, animated: true)
} else {
   let vc = MemberAdminCenterController.instance()
   controller?.navigationController?.pushViewController(vc, animated: true)
}

UIHostingController的view 在1.0中 不太适配整体页面。 不过现在已经被修复了。

国内常见的用户列表页

老规矩 先看图

看图理解 正常开发思路 tableview 上方名字时间等是headerView 下方看需求能滑就是footer不能滑就加的vc的view上。
来看看swiftUI中是怎么写的 (因为项目一直用的HandyJSON 所以处理返回的JSON时多了一次赋值。 大家理解一下,后面用的时候就不会了)

  1. 新建一个文件 选择Swift File 或者 Cocoa Touch Class 都可以 名字按照自己的需要起。
  2. 导入库 import SwiftUI import UIKit
  3. 先写个text 看看效果吧
@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {
    var body: some View {
		Text("正儿八经的VC")
    }
}

run一下 看看是不是能成功跳转了

设置navigationBar

设置nav的title (系统提供了多种样式 接下来的代码只是为了写上面的那张图)

@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {
    var body: some View {
		Text("正儿八经的VC")
		.navigationBarTitle(Text("成员管理"), displayMode: .large)
    }
}

如果你是用swiftUI搭了一个tabbar 用上面的方法设置nav是会有警告 是因为2.0已经优化了这种方法。 在tabbarItem中 加上.navigationViewStyle(StackNavigationViewStyle()) 就可以了。
有兴趣的可以去尝试一波
如果你的项目中 重写了nav 使用了自定义的 那么这时候应该就能看到 navBar上有两个返回按钮 一个是你在Base中写的 一个是系统的。如果发生了这种事情
1 在跳转时 设置原来的baseNav隐藏
2 在跳转时 直接为vc设置title
我目前用的是第二种方法,但是有些页面是动态的, 等我尝试以后在写进来
去除.navigationBarTitle(Text("成员管理"), displayMode: .large)
在跳转时加上

let v = UIHostingController(rootView: AEMemberUserAdmin())
v.title = "成员管理"

List的简单使用

设置每个用户的cell

@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {
    var body: some View {
		List {
			Text("用户1")
			Text("用户2")
		}
		.navigationBarTitle(Text("成员管理"), displayMode: .large)
    }
}

隐藏List的分割线

到了此处 就应该发现了 怎么隐藏分割线 代码来了

@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {

    var body: some View {
        List {
            Text("123123")
            Text("123123")
        }.onAppear {
            UITableView.appearance().separatorStyle = .none
        }
    }
     
}

是不是相当眼熟。
但是在14.0 没问题 在14+上 没有卵用…
所以 为了一劳永逸 在1.0和2.0中都能隐藏分割线 就是下面的代码了

@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {

    var body: some View {
        List {
            Text("123123").frame(width: UIScreen.main.bounds.size.width, height: 50, alignment: .leading)
                .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                .background(Color(.systemBackground))
            Text("123123").frame(width: UIScreen.main.bounds.size.width, height: 50, alignment: .leading)
                .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                .background(Color(.systemBackground))
        }.onAppear {
            UITableView.appearance().separatorStyle = .none
        }
    }
     
}

有点粗暴 但是能实现就好 还有一种方法 设置List的样式 但是颜色以及系统的适配 也比较麻烦
在List {}后面加上 .listStyle(SidebarListStyle()) 也能隐藏分割线
好了 然后先写个自定义的Cell

@available(iOS 14.0.0, *)
struct AEMemberCellView: View {
    
    var body: some View {
        Text("cell")
    }
    
}

这里先写死4个数据 下面会接上JSON

var body: some View {
        
        List {
            ForEach(0..<4) { idx in
                AEMemberCellView().frame(width: UIScreen.main.bounds.size.width, height: 50, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
            } 
        }.onAppear {
//            UITableView.appearance().separatorStyle = .none
            setupApi()
        }
}

List设置HeaderView

接下来设置List 的 headerView headerView是自定义的。 一看上去就知道 一个Text 肯定是不能实现的。
headerView

@available(iOS 14.0.0, *)
struct AEGroupMemberHeaderView: View {
    var body: some View {
        //  从上到下布局 
        VStack {
        	// 默认控件是居中显示的 这里使用最简单的方法让在左边显示 由于不知道名字具体长度,不使用frame来设置  往下看就知道padding 为何是8和0了  苹果默认是16
        	HStack {
                Text("学校名称").font(Font.system(size: 18, weight: .medium))
                    .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
                Spacer()
            }
            // 从左到右布局 
            HStack {
            	// siwftUI中 padding的默认值是 16 所有不用在设置间距 只需要加个弹簧 确保两个Text 一左一右
                Text("已开通园所会员老师\(2)人").font(Font.system(size: 14, weight: .medium))
                Spacer()
                Text("2022-02-24到期").font(Font.system(size: 12))
            }
        }
    }
}

给LIst 设置header

var body: some View {
        
        List {
            Section {
            	// 由于header中带有弹簧 所以直接在此处设置header的宽是屏幕宽-32
                AEGroupMemberHeaderView().frame(width: screenWidth-32, height: 80, alignment: .top)
            }
            ForEach(0..<4) { idx in
                AEMemberCellView().frame(width: UIScreen.main.bounds.size.width, height: 50, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
            }
                
        }.onAppear {
//            UITableView.appearance().separatorStyle = .none
            setupApi()
        }
    }

设置View在List的下方 并且View不能滑动

需求是 添加会员按钮 一直在页面中 所以不能为List直接设置Footer
定义添加按钮的View

@available(iOS 14.0.0, *)
struct AEGroupMemberFooterView: View {
    var body: some View {
        VStack {
            Text("还有28个会员名额").font(Font.system(size: 12)).padding(.bottom, 4)
            Button("添加园所会员") {
                debugPrint("btnClicked")
            }
            .frame(width: 180, height: 40, alignment: .center)
            .background(Color.red)
            .cornerRadius(29)
        }
    }
}

整体UI搭建完成

完整的代码

import SwiftUI
import UIKit
import HandyJSON


@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {

    var body: some View {
        List {
            Section {
                AEGroupMemberHeaderView(vo: model).frame(width: screenWidth-32, height: 80, alignment: .leading)
            }
            ForEach(0..<4) { idx in
                AEMemberCellView().frame(width: UIScreen.main.bounds.size.width, height: 50, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
            }
        }.onAppear {
//            UITableView.appearance().separatorStyle = .none
        }
        AEGroupMemberFooterView(vo: model).frame(width: screenWidth, height: 98+screenBottomHeight, alignment: .bottom)
    }
}

@available(iOS 14.0.0, *)
struct AEMemberCellView: View {
    var body: some View {
        Text("cell")
    }
}

@available(iOS 14.0.0, *)
struct AEGroupMemberHeaderView: View {
    var body: some View {
        VStack {
            HStack {
                Text(vo.title).font(Font.system(size: 18, weight: .medium))
                    .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
                Spacer()
            }
            HStack {
                Text("已开通园所会员老师\(1)人").font(Font.system(size: 14, weight: .medium))
                Spacer()
                Text("\(2)到期").font(Font.system(size: 12))
            }
        }
    }
}

@available(iOS 14.0.0, *)
struct AEGroupMemberFooterView: View {
    var body: some View {
        VStack {
            Text("还有28个会员名额").font(Font.system(size: 12)).padding(.bottom, 4)
            Button("添加园所会员") {
                debugPrint("btnClicked")
            }
            .frame(width: 180, height: 40, alignment: .center)
            .background(Color.red)
            .cornerRadius(29)
        }
    }
}

接入api 处理JSON数据

前言 SwiftUI中 要根据模型数据来展示UI 必须要给模型数据添加包装器
官网介绍数据的包装器类型—点我飞机直达
本人研究了一下 根据个人见解 说一下
声明 修饰符
1

@State

官网介绍(状态值更改时,视图会使外观无效并重新计算主体。) 常用语基础类型,也能修饰对象 但是 修饰了对象后 对象数据更新 视图不会更新。 简单理解 修饰Int Bool Double 类型。
2

@StateObject

官网介绍(当可观察对象的已发布属性更改时,SwiftUI将更新依赖于这些属性的任何视图的部分) 用于当前页面中JSON数据的修饰。 简单理解 api返回的json 在转成对象后 使用它来修饰。
3

@ObservableObject

官网介绍(它订阅可观察对象并在可观察对象发生更改时使视图无效) 和@StateObject相比 多了一个自带方法 func update() 简单理解 一次修改用它的地方都会改
4

@EnvironmentObject

官网介绍(只要可观察对象发生变化,环境对象就会使当前视图无效。如果将属性声明为环境对象,请确保通过调用其修饰符在祖先视图上设置相应的模型对象。environmentObject(_😃) 大概的一是就是 共享的JSON数据,比如用户信息 会在很多页面使用,所以使用它修饰UserJson。 但是要全局用 就得在程序初始化时声明。UIKit中使用SwiftUI时 用不到 等用swiftUI搭建App在细说。 简单理解 多页面共享一个数据。

5 其他 还有很多修饰符 等用到在给慢慢说。

页面出现时 请求网络数据 api老页面已经存在 就不用再费劲去重新写个网络请求 判断请求状态什么的了。
这里用的是MOYA

struct AEMemberUserAdmin: View { } 中写个方法 用于获取数据
AEGroupMemberVoJson 是老页面使用的model 继承自HandJSON

private func setupApi() {
        let target = BPMultiTarget.target(AEMemberApi.loadGroupMembers)
        NetProvider.provider.requestModelData(target: target, modelType: AEGroupMemberVoJson.self) { (res) in
            guard let vo = res as? AEGroupMemberVoJson else { return }
			debugPrint("\(vo.kName)")
        } failClosure: { (error) in
            BPProgressHUD.showError(error)
        }
    }

接下来就是调用方法 来获取数据了 这里就要介绍一下 swiftUI的生命周期,正常的api我们在UIKit中 在viewDidLoad() 中调用。
swiftUI的相同方法
onAppear()等于viewDidLoad()
onDisappear()等于viewDidDisappear()
理解了这个 在来看完整代码

import SwiftUI
import UIKit
import HandyJSON


@available(iOS 14.0.0, *)
struct AEMemberUserAdmin: View {
  
    var body: some View {
        
        List {
            Section {
                AEGroupMemberHeaderView(vo: model).frame(width: screenWidth-32, height: 80, alignment: .leading)
            }
            ForEach(0..<4) { idx in
                AEMemberCellView().frame(width: UIScreen.main.bounds.size.width, height: 50, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
            }
        }.onAppear {
            setupApi()
        }
        AEGroupMemberFooterView(vo: model).frame(width: screenWidth, height: 98+screenBottomHeight, alignment: .bottom)
    }
    
    private func setupApi() {
        let target = BPMultiTarget.target(AEMemberApi.loadGroupMembers)
        NetProvider.provider.requestModelData(target: target, modelType: AEGroupMemberVoJson.self) { (res) in
            guard let vo = res as? AEGroupMemberVoJson else { return }
			debugPrint("\(vo.kName)")
        } failClosure: { (error) in
            BPProgressHUD.showError(error)
        }
    } 
}

正常来讲 此时数据已经有了 需要把数据绑定到视图上。需求中要去用户信息是可更改的。所有我选择直接继承自ObservableObject 继承ObservableObject需要给定一个identifier
注:
swiftUI 使用的model 需要继承自Codable协议 如果对象没有满足Codable协议 则需要额外修饰对象中的属性 使用@Published
代码

@available(iOS 14.0.0, *)
class AEGroupMemberVo: ObservableObject {
    @Published var title = "Great Expectations"
    @Published var openedCount: Int = 0
    @Published var expiredDate: Double = 0.0
    @Published var remainedMemberNums: Int = 0

    let identifier = UUID()
}

先看cell实现 下面在贴上全部代码 以及完成后的图

cell实现

现在主流SwiftUI 设置Image 有两种 一个SDWebImage 要求iOS 13,kf 要去iOS 10.0
都是一句代码的事,项目中一直用的KF 外加作为喵神的小迷弟 就不考虑SD了
飞机直达 kf 介绍
不多比比了 直接看代码吧

@available(iOS 14.0.0, *)
struct AEMemberCellView: View {
    @StateObject var cellVo: AEGroupMemberUserVo
    
    var body: some View {
        HStack {
            Image(uiImage: UIImage(named: "user_placeholder")!)
                .resizable()
                .frame(width: 44, height: 44, alignment: .leading)
                .clipShape(Circle())
            VStack(alignment: .leading, spacing: 8) {
                HStack {
                    Text("\(cellVo.userName)").font(Font.system(size: 14))
                        .lineLimit(1) // 行数
                    
                    if cellVo.groupMemberAdmin {
                        Text("管理员").frame(width: 42, height: 16, alignment: .center)
                            .font(Font.system(size: 10))
                            .background(Color(UIColor("#EDD29C")!))
                            .foregroundColor(Color(UIColor("#71541B")!))
                    }
                }
                Text("\(cellVo.phone)").font(Font.system(size: 12))
                    .foregroundColor(Color(UIColor.textLightBlack()!))
            }
            Spacer()
            Text(getUserLever(cellVo.courseLevel)).font(Font.system(size: 12))
            Image("arrow_right_gray_small")
                .padding(.trailing, 32)
        }
    }
    
    private func getUserLever(_ lever: String) -> String {
        switch lever {
        case "L1":
            return "新手教师培训"
        case "L2":
            return "成熟教师培训"
        case "L3":
            return "骨干教师培训"
        default:
            return "管理岗教师培训"
        }
    }
}

完成图和全部代码

import SwiftUI
import UIKit
import HandyJSON



@available(iOS 14.0.0, *)

struct AEMemberUserAdmin: View {
    
    @StateObject var model: AEGroupMemberVo = AEGroupMemberVo()
        
    var body: some View {
        
        List {
            Section {
                AEGroupMemberHeaderView(vo: model).frame(width: screenWidth-32, height: 80, alignment: .leading)
            }
            ForEach(0..<model.memberList.count, id: \.self) { idx in
                AEMemberCellView(cellVo: model.memberList[idx]).frame(width: screenWidth, height: 70, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
            }
        }.onAppear {
            setupApi()
        }
        AEGroupMemberFooterView(vo: model).frame(width: screenWidth, height: 98+screenBottomHeight, alignment: .bottom)
    }
    
    
    private func setupApi() {
        let target = BPMultiTarget.target(AEMemberApi.loadGroupMembers)
        NetProvider.provider.requestModelData(target: target, modelType: AEGroupMemberVoJson.self) { (res) in
            guard let vo = res as? AEGroupMemberVoJson else { return }
            DispatchQueue.main.async {
                model.title = vo.kName
                model.openedCount = vo.openedCount
                model.expiredDate = vo.expiredDate
                model.remainedMemberNums = vo.remainedMemberNums
                var list: [AEGroupMemberUserVo] = []
                for item in vo.memberList {
                    let member = AEGroupMemberUserVo()
                    member.userId = item.userId
                    member.userName = item.userName
                    member.phone = item.phone
                    member.groupMemberAdmin = item.groupMemberAdmin
                    member.courseLevel = item.courseLevel
                    list.append(member)
                }
                model.memberList = list
            }
        } failClosure: { (error) in
            BPProgressHUD.showError(error)
        }
    }
    
    
}

@available(iOS 14.0.0, *)
struct AEMemberCellView: View {
    @StateObject var cellVo: AEGroupMemberUserVo
    
    var body: some View {
        HStack {
            Image(uiImage: UIImage(named: "user_placeholder")!)
                .resizable()
                .frame(width: 44, height: 44, alignment: .leading)
                .clipShape(Circle())
            VStack(alignment: .leading, spacing: 8) {
                HStack {
                    Text("\(cellVo.userName)").font(Font.system(size: 14))
                        .lineLimit(1)
                    
                    if cellVo.groupMemberAdmin {
                        Text("管理员").frame(width: 42, height: 16, alignment: .center)
                            .font(Font.system(size: 10))
                            .background(Color(UIColor("#EDD29C")!))
                            .foregroundColor(Color(UIColor("#71541B")!))
                    }
                }
                Text("\(cellVo.phone)").font(Font.system(size: 12))
                    .foregroundColor(Color(UIColor.textLightBlack()!))
            }
            Spacer()
            Text(getUserLever(cellVo.courseLevel)).font(Font.system(size: 12))
            Image("arrow_right_gray_small")
                .padding(.trailing, 32)
        }
    }
    
    
    private func getUserLever(_ lever: String) -> String {
        switch lever {
        case "L1":
            return "新手教师培训"
        case "L2":
            return "成熟教师培训"
        case "L3":
            return "骨干教师培训"
        default:
            return "管理岗教师培训"
        }
    }
    
}

@available(iOS 14.0.0, *)
struct AEGroupMemberHeaderView: View {
    @StateObject var vo: AEGroupMemberVo
    
    var body: some View {
        VStack {
            HStack {
                Text(vo.title).font(Font.system(size: 18, weight: .medium))
                    .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
                Spacer()
            }
            HStack {
                Text("已开通园所会员老师\(vo.openedCount)人").font(Font.system(size: 14, weight: .medium))
                Spacer()
                Text("\(vo.expiredDate.dateDayString)到期").font(Font.system(size: 12))
            }
        }
        
        
    }
}

@available(iOS 14.0.0, *)
struct AEGroupMemberFooterView: View {
    @StateObject var vo: AEGroupMemberVo
    
    var body: some View {
        VStack {
            HStack {
                Text("还有")
                    .foregroundColor(Color(UIColor.textLightBlack()!))
                    + Text("\(vo.remainedMemberNums)个")
                    .foregroundColor(Color(UIColor("#C08C26")!))
                    + Text("会员名额")
                    .foregroundColor(Color(UIColor.textLightBlack()!))
            }.padding(.bottom, 4).font(Font.system(size: 12))
            
        
            Button("添加园所会员") {
                debugPrint("btnClicked")
            }
            .frame(width: 225, height: 44, alignment: .center)
            .background(Color(UIColor("#F1D8A4")!))
            .cornerRadius(22)
            .foregroundColor(Color(UIColor("#71541B")!))
        }
    }
}



@available(iOS 14.0.0, *)
class AEGroupMemberVo: ObservableObject {
    @Published var title = "Great Expectations"
    @Published var openedCount: Int = 0
    @Published var expiredDate: Double = 0.0
    @Published var remainedMemberNums: Int = 0
    @Published var memberList: [AEGroupMemberUserVo] = []

    let identifier = UUID()
}
@available(iOS 14.0.0, *)
class AEGroupMemberUserVo: ObservableObject {
    
    @Published var userId: Int = -1
    @Published var userName: String = ""
    @Published var phone: String = ""
    @Published var groupMemberAdmin: Bool = false
    @Published var courseLevel: String = ""

    let identifier = UUID()
}

完善SwiftUI点击跳UIKitVC

成员编辑页已经有了,在不改成swiftUI的情况下跳转。 编辑页在成功信息编辑成功后 会进行闭包回调。用于刷新上个页面
1 View中点击button进行跳转

@available(iOS 14.0.0, *)
struct AEGroupMemberFooterView: View {
    
    @StateObject var vo: AEGroupMemberVo
    // 新增成员的回调
    public var addBlock: (()->Void)?
    
    var body: some View {
        VStack {
            HStack {
                Text("还有")
                    .foregroundColor(Color(UIColor.textLightBlack()!))
                    + Text("\(vo.remainedMemberNums)个")
                    .foregroundColor(Color(UIColor("#C08C26")!))
                    + Text("会员名额")
                    .foregroundColor(Color(UIColor.textLightBlack()!))
            }.padding(.bottom, 4).font(Font.system(size: 12))
            Button("添加园所会员") {
                addMemberClick()
            }
            .frame(width: 225, height: 44, alignment: .center)
            .background(Color(UIColor("#F1D8A4")!))
            .cornerRadius(22)
            .foregroundColor(Color(UIColor("#71541B")!))
        }.padding(.top, 24)
    }

    private func addMemberClick() {
        if vo.remainedMemberNums == 0 {
            BPProgressHUD.showToast(text: "没有名额了")
            return
        }
        let vc = MemberG1AdminAddViewController.instance()
        vc.isHasCourseAuthority = vo.hasCourseAuthority
        vc.addMemberSuccess = addBlock
        // 获取最顶层的NavigationController 一般项目中应该都有这个方法
        getCurrentNavigationController()?.pushViewController(vc, animated: true)
    }
}

在footer中添加了 闭包 在用footer的地方会自动提示

AEGroupMemberFooterView(vo: <#T##AEGroupMemberVo#>, addBlock: <#T##(() -> Void)?##(() -> Void)?##() -> Void#>)

整体body代码

	var body: some View {
        
        List {
            Section {
                AEGroupMemberHeaderView(vo: model).frame(width: screenWidth-32, height: 80, alignment: .leading)
            }
            ForEach(0..<model.memberList.count, id: \.self) { idx in
                AEMemberCellView(cellVo: model.memberList[idx]).frame(width: screenWidth, height: 70, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
                    .onTapGesture {
                        editMemberUser(idx)
                    }
            }
        }.onAppear {
            setupApi()
        }
        AEGroupMemberFooterView(vo: model) {
            setupApi()
        }.frame(width: screenWidth, height: 98+screenBottomHeight, alignment: .top) 
    }

List的 item 点击响应事件

系统自带的是 直接传一个body 进行跳转 反正没啥用 除了死页面会用。
这里使用手势 获取响应事件 做一些判断在执行跳转

		List {
            Section {
                AEGroupMemberHeaderView(vo: model).frame(width: screenWidth-32, height: 80, alignment: .leading)
            }
            ForEach(0..<model.memberList.count, id: \.self) { idx in
                AEMemberCellView(cellVo: model.memberList[idx]).frame(width: screenWidth, height: 70, alignment: .leading)
                    .listRowInsets(EdgeInsets(top: -1, leading: 16, bottom: -1, trailing: 16))
                    .background(Color(.systemBackground))
                    .onTapGesture {
                    // 手势响应事件
                        editMemberUser(idx)
                    }
            }
        }.onAppear {
            setupApi()
        }

跳转的也是已经有的 编辑成员也 就不贴代码了。 就是用idx获取list 在进行跳转

SwiftUI 刷新界面

@StateObject 只能刷新一次UI
所以需要修改 @StateObject 改为 @ObservedObject 注意凡是需要动态刷新的地方修饰符都要修改
1 修改修饰符

@ObservedObject var model: AEGroupMemberVo = AEGroupMemberVo()

2 model中调用willSet 方法

@available(iOS 14.0.0, *)
class AEGroupMemberVo: ObservableObject {
    @Published var title = "Great Expectations"
    @Published var openedCount: Int = 0
    @Published var expiredDate: Double = 0.0
    @Published var remainedMemberNums: Int = 0
    @Published var hasCourseAuthority: Bool = false
    @Published var memberList: [AEGroupMemberUserVo] = [] {
        willSet {
            objectWillChange.send()
        }
    }
        
    // 编辑页要使用
    @Published var memberJsonList: [AEMemberUserVoJson] = []

//    let identifier = UUID()
}

@available(iOS 14.0.0, *)
class AEGroupMemberUserVo: ObservableObject {
    
    @Published var userId: Int = -1
    @Published var userName: String = ""
    @Published var phone: String = ""
    @Published var groupMemberAdmin: Bool = false
    @Published var courseLevel: String = "" {
        willSet {
            objectWillChange.send()
        }
    }
//    let identifier = UUID()
}

结束了。 一个页面完成
最后 吐槽一下 都2.0的 写个sRGB颜色 还得自己去扩充方法 隔壁安卓几年前就支持 直接是用十六进制颜色表了。 真…
由于我项目里本来 UIColor 就做了扩展 就没在写了。 网上搜一下 很多。 因为支持 Color(UIColor(xx)) 我就直接用了

使用SwiftUI搭建一个app`

明天在写用SwiftUI搭建一个App 下班了

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值