简体   繁体   中英

How do I modify the background color and shape of drag&drop preview SwiftUI?

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:

List item view:

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))
    }
}

First solution (using List):

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:

使用 ScrollView 拖放预览

Second solution (using ScrollView)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM