提问人:Nicolas Gimelli 提问时间:1/31/2023 最后编辑:Nicolas Gimelli 更新时间:8/26/2023 访问量:702
在 SwiftUI 中使用 onMove 拖动(和长按)时如何去除列表项的背景?
How to remove the background of list items when dragging (and when long-pressing) with onMove in SwiftUI?
问:
我在 SwiftUI 中有一个列表,如下所示:
@State var items = ["item0", "item1", "item2"]
List {
ForEach(items, id: \.self) { item in
ZStack {
Rectangle()
.frame(width: 150, height: 65)
Text(item)
}
.onMove { from, to in
items.move(fromOffsets: from, toOffset: to)
}
}
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
这个想法是在 ForEach 中显示项目,就好像它们只是“浮动”在屏幕上,而不是在列表中。
但是,当我拖动列表项时(在列表中重新排序;从 .onMove() 修饰符),会出现白色背景:
我想删除这个背景,所以看起来我只是在移动列表项(只是圆角矩形,没有背景)。我怎样才能做到这一点?
编辑:我添加了在主动拖动时删除背景的功能,但是当我们长按项目时,在我们开始拖动之前,它仍然会出现。.onDrag {} \@ViewBuilder preview: {}
答:
-5赞
HelloWorld
2/7/2023
#1
另一种方法:使用部分(这可能是重新排序时背景的原因)
var body: some View {
List {
ForEach(self.items, id: \.self) { item in
Section {
Text(item)
.padding(.all)
.listStyle(.plain)
.foregroundColor(.white)
.listRowBackground(Color.blue)
.cornerRadius(12)
}
.listSectionSeparator(.automatic)
.listSectionSeparatorTint(.clear)
.listStyle(.plain)
}
.onMove(perform: move)
}
.scrollContentBackground(.hidden)
}
func move(from source: IndexSet, to destination: Int) {
items.move(fromOffsets: source, toOffset: destination)
}
- 不需要ZStack,配置文本
- 需要处理动画和边缘情况,但没有背景温重新排序
0赞
narek.sv
8/26/2023
#2
出现白色背景,因为 中的默认单元格选择类型,很遗憾,目前无法修改。List
解决方法是手动使用和组合并实现重新排序逻辑。ScrollView
LazyVStack
像这样的东西
import SwiftUI
import UniformTypeIdentifiers
/// This represents a simple item in your list
struct ListItem: View {
let item: String
var body: some View {
ZStack {
Rectangle()
.frame(width: 150, height: 65)
Text(item)
}
}
}
/// We need custom NSItemProvider to track when the drop finishes
final class ListItemProvider: NSItemProvider {
var onCompletion: (() -> ())?
deinit {
onCompletion?()
}
}
/// ViewModel to handle the state changes
/// You can emit this and simply use @State objects in your view
/// as you have done in your example
final class ListViewModel: ObservableObject {
@Published private(set) var items = ["item0", "item1", "item2"]
@Published private(set) var draggedItem: String?
func shouldHighlight(item: String) -> Bool {
return draggedItem == item
}
func move(to item: String) {
guard let draggedItem,
let from = items.firstIndex(of: draggedItem),
let to = items.firstIndex(of: item),
item != draggedItem else { return }
items.move(fromOffsets: IndexSet(integer: from), toOffset: to > from ? to + 1 : to)
}
func startInteraction(item: String) -> NSItemProvider {
let provider = ListItemProvider(item: nil, typeIdentifier: item)
provider.onCompletion = { [weak self] in
DispatchQueue.main.async {
self?.draggedItem = nil
}
}
if draggedItem == nil && items.contains(item) {
draggedItem = item
}
return provider
}
}
/// Custom drop delegate to implement custom reordering logic
struct ListDropDelegate: DropDelegate {
let item: String
@ObservedObject var viewModel: ListViewModel
func performDrop(info: DropInfo) -> Bool {
viewModel.shouldHighlight(item: item)
}
func dropUpdated(info: DropInfo) -> DropProposal? {
.init(operation: .move)
}
func dropEntered(info: DropInfo) {
withAnimation(.default) {
self.viewModel.move(to: self.item)
}
}
}
/// And finally the view
struct ContentView: View {
@StateObject var viewModel: ListViewModel = .init()
var body: some View {
ScrollView {
LazyVStack {
ForEach(viewModel.items, id: \.self) { item in
ListItem(item: item)
.opacity(viewModel.shouldHighlight(item: item) ? 0 : 1)
.onDrag { viewModel.startInteraction(item: item) }
.onDrop(of: [.text], delegate: ListDropDelegate(item: item, viewModel: viewModel))
}
}
}
}
}
评论