I'm trying to implement simple list with draggable cells (iOS 15). For now I have two base solutions: List and ScrollView. None of this methods doesn't lead me to appropriate result.
First of all I'll provide some code:
struct ListItemView: View {
let index: Int
var body: some View {
HStack {
Text("Item \(index)")
.foregroundColor(.white)
Spacer()
}
.padding()
.background(Color.gray)
.clipShape(RoundedRectangle(cornerRadius: 16.0))
}
}
struct ContentView: View {
var body: some View {
List(0 ..< 10) { i in
ListItemView(index: i)
.onDrag {
NSItemProvider(object: String(describing: "item_\(i)") as NSString)
}
.padding(.bottom, 8.0)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8))
}
.background(Color.black)
.listStyle(PlainListStyle())
}
}
The result is fine, but when I start dragging action, I see some white rect around my cell, which is not what I want:
struct ContentView: View {
var body: some View {
ScrollView {
ForEach(0 ..< 10) { i in
ListItemView(index: i)
.onDrag {
NSItemProvider(object: String(describing: "item_\(i)") as NSString)
}
}
}
.background(Color.black)
}
}
The result is a bit different: we don't see any coloured rects (I suppose, this is because we are not using List and there is different mechanism of drag&drop feature). Also there is scaled preview and shape without rounded corners.
So, the desirable behaviour is: a) the size of preview is the same as original size of list item b) there is rounded-corners shape without white frame
How can I achieve it?
Theoretically your NSItemProvider
can offer a previewImageHandler
which is a block that draws the image you want the system to use as the drag preview. Unfortunately that doesn't seem to work. The documentation of the onDrag
modifier says that it uses a picture of the view as the preview image and it seemingly ignores the previewImageHandler
. If someone finds out otherwise, leave a comment and I'll amend the answer. Here's what I tried:
import SwiftUI
struct ListItem : Identifiable {
let id: Int
let title: String
}
let itemsInList = (0..<10).map { ListItem(id: $0, title: "Item \($0)") }
@ViewBuilder func ListItemView(title : String) -> some View {
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: 18, style: .circular)
.foregroundColor(Color(red: 142.0/255.0, green: 142.0/255.0, blue: 142.0/255.0, opacity: 1.0))
Text(title)
.foregroundColor(.white)
.padding()
}
}
struct ContentView: View {
var body: some View {
List(itemsInList) {
let itemTitle = $0.title
ListItemView(title: itemTitle)
.onDrag {
let itemProvider = NSItemProvider()
itemProvider.registerObject("Item \(itemTitle)" as NSString, visibility: .all)
itemProvider.previewImageHandler = {
(completionHandler, expectedClass, options) -> Void in
var resultImage : CGImage?
debugPrint(expectedClass ?? "Unknown")
if let cgContext = CGContext(data: nil,
width: 72,
height: 72,
bitsPerComponent: 8,
bytesPerRow: 16 * (((72 * 4) + 15) / 16),
space: CGColorSpace(name: CGColorSpace.sRGB)!,
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) {
let frame = CGRect(x: 0, y: 0, width: 72, height: 72)
cgContext.clear(frame)
cgContext.setFillColor(UIColor.red.cgColor)
cgContext.fillEllipse(in: frame)
resultImage = cgContext.makeImage()
}
if let completionHandler = completionHandler {
if let resultImage = resultImage {
completionHandler(UIImage(cgImage: resultImage), nil)
} else {
completionHandler(nil, NSError(domain: "ItemCompletion", code: -1, userInfo: nil))
}
}
}
return itemProvider
}
}
}
}
struct previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
I had (and now have) the same issue as you describe. When dragging, the corners of my items are not rounded anymore and the items in general are cut off (as if i would have called clipped()
on them). My workaround up untill iOS 15 was to remove the preview:
.onDrag({
current = item
return NSItemProvider(object: "\(item.id)" as NSString)
}, preview: {
Text(" ") // replace eventually ...
})
The empty text view here works better than an EmptyView()
, as it fully removes any preview (instead of a small indicator).
However, with iOS 16 , this solution does not work anymore (they added some initial drag animation) and even displays a warning ( CLIENT APP ERROR - Neither the view or container of the UITargetedPreview is currently in a window. This is in violation of UIDragInteraction API contract and can cause a severe visual glich. THIS IS A CLIENT APP BUG and will soon be a hard assert. PLEASE FIX ME
).
Therefore, did you find any solution yet?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.