Ish*_*war 5 swiftui swiftui-list
我正在尝试在 SwiftUI 中编写一个“单视图应用程序”。主要设计非常简单。我有一个项目列表(例如费用),我在导航视图 - >列表的主视图中显示它。
列表查看源代码
import SwiftUI
struct AmountBasedModifier : ViewModifier{
var amount: Int
func body(content: Content) -> some View {
if amount <= 10{
return content.foregroundColor(Color.green)
}
else if amount <= 100{
return content.foregroundColor(Color.blue)
}
else {
return content.foregroundColor(Color.red)
}
}
}
extension View {
func amountBasedStyle(amount: Int) -> some View {
self.modifier(AmountBasedModifier(amount: amount))
}
}
struct ExpenseItem: Identifiable, Codable {
var id = UUID()
var name: String
var type: String
var amount: Int
static var Empty: ExpenseItem{
return ExpenseItem(name: "", type: "", amount: 0)
}
}
class Expenses: ObservableObject {
@Published var items = [ExpenseItem](){
didSet{
let encoder = JSONEncoder()
if let data = try? encoder.encode(items){
UserDefaults.standard.set(data, forKey: "items")
}
}
}
init() {
let decoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "items"){
if let items = try? decoder.decode([ExpenseItem].self, from: data){
self.items = items
return
}
}
items = []
}
}
struct ContentView: View {
@ObservedObject var expenses = Expenses()
@State private var isShowingAddNewItemView = false
var body: some View {
NavigationView{
List{
ForEach(self.expenses.items) { item in
NavigationLink(destination: ExpenseItemHost(item: item, expenses: self.expenses)){
HStack{
VStack(alignment: .leading){
Text(item.name)
.font(.headline)
Text(item.type)
.font(.subheadline)
}
Spacer()
Text("$\(item.amount)")
.amountBasedStyle(amount: item.amount)
}
}
}.onDelete(perform: removeItems)
}
.navigationBarTitle("iExpense")
.navigationBarItems(leading: EditButton(), trailing: Button(action:
{
self.isShowingAddNewItemView.toggle()
}, label: {
Image(systemName: "plus")
}))
.sheet(isPresented: $isShowingAddNewItemView) {
AddNewExpense(expenses: self.expenses)
}
}
}
func removeItems(at offsets: IndexSet){
self.expenses.items.remove(atOffsets: offsets)
}
}
Run Code Online (Sandbox Code Playgroud)
每个行项目都是 NavigationLink,它以只读模式打开费用,显示费用项目的所有属性。
右上角有一个添加按钮,让用户在列表中添加新的费用项目。AddNewExpenseView(显示为工作表)可以访问列表数据源。因此,每当用户添加新费用时,列表的数据源就会更新(通过附加新项目),并且工作表将被关闭。
添加查看源代码
struct AddNewExpense: View {
@ObservedObject var expenses: Expenses
@Environment(\.presentationMode) var presentationMode
@State private var name = ""
@State private var type = "Personal"
@State private var amount = ""
@State private var isShowingAlert = false
static private let expenseTypes = ["Personal", "Business"]
var body: some View {
NavigationView{
Form{
TextField("Name", text: $name)
Picker("Expense Type", selection: $type) {
ForEach(Self.expenseTypes, id: \.self) {
Text($0)
}
}
TextField("Amount", text: $amount)
}.navigationBarTitle("Add New Expense", displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
if let amount = Int(self.amount){
let expenseItem = ExpenseItem(name: self.name, type: self.type, amount: amount)
self.expenses.items.append(expenseItem)
self.presentationMode.wrappedValue.dismiss()
}else{
self.isShowingAlert.toggle()
}
}, label: {
Text("Save")
}))
.alert(isPresented: $isShowingAlert) {
Alert.init(title: Text("Invalid Amount"), message: Text("The amount should only be numbers and without decimals"), dismissButton: .default(Text("OK")))
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
费用明细(只读)查看源代码
struct ExpenseItemView: View {
var item: ExpenseItem
var body: some View {
List{
Section{
Text("Name")
.font(.headline)
Text(item.name)
}
Section{
Text("Expense Type")
.font(.headline)
Text(item.type)
}
Section{
Text("Amount")
.font(.headline)
Text("$\(item.amount)")
}
}.listStyle(GroupedListStyle())
.navigationBarTitle(Text("Expense Details"), displayMode: .inline)
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都很好。然后我想到在 ExpenseItem View 屏幕上添加一个 Edit 按钮,以便用户可以编辑费用。我创建了一个编辑视图,当单击“编辑”按钮时,该编辑视图会从只读视图中作为工作表启动。
编辑查看代码
struct ExpenseItemHost: View {
@State var isShowingEditSheet = false
@State var item: ExpenseItem
@State var itemUnderEdit = ExpenseItem.Empty
var expenses: Expenses
var body: some View {
VStack{
ExpenseItemView(item: self.item)
}
.navigationBarItems(trailing: Button("Edit")
{
self.isShowingEditSheet.toggle()
})
.sheet(isPresented: $isShowingEditSheet) {
EditExpenseItemView(item: self.$itemUnderEdit)
.onAppear(){
self.itemUnderEdit = self.item
}
.onDisappear(){
//TO DO: Handle the logic where save is done when user has explicitly pressed "Done" button. `//Presently it is saving even if Cancel button is clicked`
if let indexAt = self.expenses.items.firstIndex( where: { listItem in
return self.item.id == listItem.id
}){
self.expenses.items.remove(at: indexAt)
}
self.item = self.itemUnderEdit
self.expenses.items.append(self.item)
}
}
}
}
struct EditExpenseItemView: View {
@Environment(\.presentationMode) var presentationMode
@Binding var item: ExpenseItem
static private let expenseTypes = ["Personal", "Business"]
var body: some View {
NavigationView{
Form{
TextField("Name", text: self.$item.name)
Picker("Expense Type", selection: self.$item.type) {
ForEach(Self.expenseTypes, id: \.self) {
Text($0)
}
}
TextField("Amount", value: self.$item.amount, formatter: NumberFormatter())
}
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarItems(leading: Button("Cancel"){
self.presentationMode.wrappedValue.dismiss()
}, trailing: Button("Done"){
self.presentationMode.wrappedValue.dismiss()
})
}
}
}
Run Code Online (Sandbox Code Playgroud)
截图
问题
我希望当用户通过按“完成”按钮完成编辑时,工作表应返回到“只读”屏幕,因为这是用户单击“编辑”按钮的位置。但由于单击“完成”按钮时我正在修改 ListView 的数据源,因此 ListView 正在重新创建/刷新。因此,单击“完成”按钮时将显示 ListView,而不是 EditView 工作表返回到 ReadOnly 视图。
由于我的代码正在更改用户现在无法访问的视图的数据源,因此也会生成以下异常
2020-08-02 19:30:11.561793+0530 iExpense[91373:6737004] [TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: <_TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView: 0x7f9a8b021800; baseClass = UITableView; frame = (0 0; 414 896); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x6000010a1110>; layer = <CALayer: 0x600001e8c0e0>; contentOffset: {0, -140}; contentSize: {414, 220}; adjustedContentInset: {140, 0, 34, 0}; dataSource: <_TtGC7SwiftUIP13$7fff2c9a5ad419ListCoreCoordinatorGVS_20SystemListDataSourceOs5Never_GOS_19SelectionManagerBoxS2___: 0x7f9a8a5073f0>>
Run Code Online (Sandbox Code Playgroud)
我可以理解为什么 ListView 刷新被触发,但我无法弄清楚编辑模型的正确模式,以及当我们在中间屏幕之间(即列表视图 -> 只读 -> 编辑)时不会导致 ListView 刷新触发看法。
处理此案有何建议?
| 归档时间: |
|
| 查看次数: |
398 次 |
| 最近记录: |