swiftui - 单击列表项上的按钮与整个项重叠
问题描述
我创建了一张卡片列表,每张卡片都有一个执行操作的按钮。但是当我在测试时,我注意到我可以点击卡片的位置也可以执行按钮的操作。这是我在 switf 上的第一个应用程序,所以我没有任何经验。有人可以帮忙吗?
这是已编辑的项目视图:
typealias changeStatusAlias = (OrderDTO, String, String, String?) -> Void
struct OrderItem: Identifiable {
let id = UUID()
let order: OrderDTO
}
struct ItemOrderRiderView: View {
private let item: OrderItem
@State private var showMethodPicker = false
private let changeState: changeStatusAlias
private let showDialogPayment: (OrderDTO)-> Void
private let orderValue: String?
@ObservedObject var locationManager = LocationManager()
init(item: OrderItem, changeState: @escaping changeStatusAlias, showDialogPayment: @escaping (OrderDTO)-> Void) {
let formatter = NumberFormatter()
self.item = item
formatter.numberStyle = NumberFormatter.Style.currencyAccounting
formatter.locale = Locale(identifier: "DE")
formatter.currencyCode = "eur"
self.changeState = changeState
self.showDialogPayment = showDialogPayment
orderValue = formatter.string(from: NSNumber(value: item.order.value))
}
var body: some View {
ZStack {
VStack {
HStack{
Text(item.order.orderNumber)
.fontWeight(.bold)
.padding(.leading, /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
Spacer()
Text(item.order.subStateName)
.padding(.trailing,10)
}.padding(.top, 5)
HStack{
Text(item.order.commercialName)
.fontWeight(.bold)
.padding(.leading, /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
Spacer()
Text(item.order.deliveryHour)
.padding(.trailing,10)
}.padding(.top, 5)
Text(item.order.user)
.padding(.leading, 10)
.padding(.top, 10)
.frame(maxWidth: .infinity,alignment: .leading)
Text(item.order.stateName)
.padding(.leading, 10)
.padding(.top, 1)
.frame(maxWidth: .infinity,alignment: .leading)
Text(orderValue!)
.padding(.leading, 10)
.padding(.top, 1)
.frame(maxWidth: .infinity,alignment: .leading)
HStack {
Image("pin_grey")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
Text("\(item.order.routeDistance) km")
.foregroundColor(Color(hue: 1.0, saturation: 0.045, brightness: 0.397))
.font(.system(size: 14))
Spacer()
Image("watch_grey")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
Text("\(item.order.routeTime) min")
.foregroundColor(Color(hue: 1.0, saturation: 0.045, brightness: 0.397))
.font(.system(size: 14))
Button(action: {
self.locationManager.locationString = item.order.completeAddress ?? ""
self.locationManager.openMapWithAddress()
}, label: {
Image("map_loc_red")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.padding(.leading, 30)
})
Spacer()
Button(action: {
}, label: {
Image("pin_quick_red")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.padding(.leading, 20)
})
Spacer()
Image("info_black")
.resizable()
.scaledToFit()
.frame(width: 30, height: 30, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.padding(.leading, 20)
}.padding(.bottom, 10)
Button(
action: {
switch item.order.riderActions.getRequestValueState(){
case "delivered":
if(item.order.orderState == "quickdelivery_cash") {
showDialogPayment(item.order)
} else {
changeState(
item.order,
item.order.riderActions.getRequestValueState(),
SubStateAction.Companion().SUB_STATE_FIELD,
nil
)
}
break
default:
changeState(
item.order,
item.order.riderActions.getRequestValueState(),
SubStateAction.Companion().SUB_STATE_FIELD,
nil
)
break
}
}) {
Text(item.order.riderActions.getTitle().localized())
}
.frame(width: 300, height: 40, alignment: .bottom)
}.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(.sRGB, red: 150/255, green: 150/255, blue: 150/255, opacity: 0.1), lineWidth: 1)
)
.padding([.top, .horizontal])
.alert(isPresented: $locationManager.invalid) {
Alert(title: Text("Important message"), message: Text("Enter a valid address"), dismissButton: .default(Text("OK"), action:{
locationManager.invalid = false
locationManager.locationString = ""
}))
}
}
}
}
struct ItemOrderRiderView_Previews: PreviewProvider {
static var previews: some View {
ItemOrderRiderView(
item: OrderItem(order: mockedOrder),
changeState: {(mock1,mock2,mock3,mock4) in},
showDialogPayment: {mock1 in}
)
.previewLayout(.fixed(width: 400, height: 400))
}
}
private let mockedOrder = OrderDTO(
id: 0001,
orderNumber: "0001",
user: "Teste Da Silva",
kitchenStateName: "ready",
stateName: "quickdelivery cash",
subStateName: "in progress",
deliveryHour: "10:00 - 11:55",
value: 100,
type: LoginLabel.Companion().loginButton,
action: [ResourcesStringDesc](),
commercialName: "Restaurante teste",
riderActions: SubStateAction.delivered,
routeDistance: "100",
routeTime: "5",
completeAddress: "rua teste 12",
googleAddress: "rua teste 12",
userPhone: "999999999",
restaurantLocation: Localization(latitude:0,longitude:0),
riderId: 2312,
orderState: "quickdelivery_cash",
deliveryLocation: Localization(latitude: 0, longitude: 0),
deliveryStateString: "delivery_in_progress",
commercialPremiseId: 12,
riderName: "Joao da Silva"
)
更新:我实现了其他按钮操作,我也遇到了同样的问题,现在当我点击卡片时,它会执行所有按钮点击操作,所以当我添加一个新按钮时,它也会在我点击卡片。
这就是我实施列表的方式:
struct DeliveryManagerView: View {
@ObservedObject private var viewModel: OrderViewModel
@ObservedObject var profileViewModel: ProfileViewModel
init(repository: OrderRepository, profileViewModel: ProfileViewModel) {
self.viewModel = OrderViewModel(repository: repository)
self.profileViewModel = profileViewModel
}
let timer = Timer.publish(every: 30, on: .main, in: .common).autoconnect()
@State private var showPaymentAlert = false
@State private var order:OrderDTO? = nil
var body: some View {
GeometryReader { proxy in
ZStack(alignment: .bottom) {
List(viewModel.ordersItems) { item in
ItemOrderRiderView(
item: item,
changeState: { (order, request, stateField, paymentMethod) in
viewModel.changeOrderState(
orderDTO: order,
request: request,
stateField: stateField,
paymentMethod: paymentMethod
)
},
showDialogPayment: {order in
self.order = order
self.showPaymentAlert = true
}
)
}.onAppear(perform: {
manageConfigRequests()
})
.padding(.bottom, proxy.safeAreaInsets.top)
if(viewModel.loading){
ActivityIndicator()
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(.orange)
}
}.padding(.bottom, proxy.safeAreaInsets.top)
.customDialog(isShowing: $showPaymentAlert, dialogContent: {
PaymentMethodAlert { method in
viewModel.changeOrderState(
orderDTO: self.order!,
request: self.order!.riderActions.getRequestValueState(),
stateField: SubStateAction.Companion().SUB_STATE_FIELD,
paymentMethod: method
)
self.showPaymentAlert = false
}
})
}.onReceive(timer, perform: { _ in
viewModel.getOrders(profile: profileViewModel.defaultProfile!)
})
}
private func manageConfigRequests() {
if(profileViewModel.defaultProfile != nil) {
viewModel.getOrders(profile: profileViewModel.defaultProfile!)
} else {
profileViewModel.getProfile()
}
if(viewModel.changedState) {
viewModel.getOrders(profile: profileViewModel.defaultProfile!)
}
}
}
解决方案
看起来 List() 有这个限制,整个项目都是可点击的。所以我解决了将 List 更改为 ScrollView() 并使用 ForEach() 的问题:
ScrollView {
ForEach(viewModel.ordersItems, id: \.self) {
item in
ItemOrderRiderView(
item: item,
changeState: { (order, request, stateField, paymentMethod) in
viewModel.changeOrderState(
orderDTO: order,
request: request,
stateField: stateField,
paymentMethod: paymentMethod
)
},
showDialogPayment: {order in
self.order = order
self.showPaymentAlert = true
}
)
}
}.onAppear(perform: {
manageConfigRequests()
})
.padding(.bottom, proxy.safeAreaInsets.top)
推荐阅读
- c++ - Swig:使用 weak_ptr 时的简单惯用包装器用法?
- c# - 缺少引用.net 4.7.2框架测试项目的依赖项引用.net 4.7 Win表单项目引用.net标准2.0库
- javascript - 随机选择谁送礼物给谁的程序。香草JS
- php - 如何显示来自 2 个同名行但不同表的数据?
- javascript - html 蛇类游戏的 JavaScript 点计数器
- react-hook-form - react-hook-form:需要验证其中一个字段
- javascript - React 状态不会从 useCallback 中更新
- javascript - 使用类验证 nodejs 模型
- activex - 升级到 C++Builder 11 后创建 ActiveX 控件时访问冲突
- javascript - 过滤器中的节点js异步调用导致404错误