Get frame of Drag preview

南楼画角 提交于 2020-05-28 14:53:19

问题


I'm implementing drag-n-drop on UICollectionView using a new Apple's API introduced in iOS 11.

What I need is to get a frame of drag preview (see below, red rectangle)

I tried to get drag's location:

extension ViewController: UICollectionViewDropDelegate {
    func collectionView(_ collectionView: UICollectionView, 
         dropSessionDidUpdate session: UIDropSession, 
         withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
        let location = session.location(in: collectionView)
    }

but it represents my finger's location only. I need to get the entire frame or, at least, it's origin.

I also tried to store y position of touch in my cell and then do something like dragLocation.y - touchLocationInCell.y, but this approach is not accurate: looks like the preview gets some offset from the touch location.


回答1:


I have worked on similar functionality and can share some details. To be short, it seems that there is no option that allows position tracking of preview view.

From my perspective, I would not rely on the frame of the preview view. There are some cases when system could make preview view smaller or bigger than original view. For instance, it could happen when initial dragged view size is bigger than it's super view size. In this case system will make preview view smaller in order to bring more visibility about content that is being dragged. And this behavior is not predictable, system could apply such transformation based on some internal logic.

I think the way it works is because drag and drop engine allows to move items not only inside one specific app but between other apps. Since that, drag and drop engine should be a separate system process that handles all drag and drop operations including preview sizing and position.

During drag operation, system creates either original view snapshot or snapshot of the view that was returned from previewProvider of UIDragItem.

Then it creates container view (instance of private UIPortalView class with CAPortalLayer underneath), installs snapshot into this container and displays container view. Since that moment preview view position is handled by system and there is no public API to access this view.

The only possible way to track drag position is to use location(in view: UIView) function of UIDragDropSession protocol.

I hope this helps.




回答2:


I am facing the same issue (trying to drag/drop a UICollectionView calendar event cell) and I've come to the conclusion (as did Alex D.) that the iOS 11 provided "preview" view cannot solve this problem. However, I did find a decent alternative solution, which is basically to create your own preview view from a UICollectionViewCell.snapshot. Then you can drag that view around and you'll have access to its position, and once the user "drops" the view, create the new cell based on the custom preview view's position and remove the custom preview view.

You can see a working example of this here: https://github.com/zjfjack/JZCalendarWeekView/blob/master/JZCalendarWeekView/JZLongPressWeekView.swift#L385




回答3:


I was facing the same issue. I went through all the code Apple provides but could not find anything that would provide information on the preview frame. What I ended up doing is storing the initial position of the cell and then moving the origin by the displacement of the initial touch and the final touch. Something like this

func finalFrame(at location: CGPoint) -> CGRect {
    var displacement: CGPoint = CGPoint(x: 0, y: 0)
    displacement.x = location.x - initialTapLocation.x
    displacement.y = location.y - initialTapLocation.y

    var newFrame = initialCellFrame
    newFrame.origin.x += displacement.x
    newFrame.origin.y += displacement.y

    return newFrame
}

Not the ideal solution, specifically for custom previews with scaling animation, but I hope this helps. The location you can get from session.location:in and I am storing the initial location in custom coordinator type, but you can use local object or context for that.

You can get the initial position and frame in the

collectionView:dragSessionWillBegin

OR

collectionView:itemsForBeginning:at

with

let location = session.location(in: collectionView) //store this

//if using the second method, it provides the indexpath, so no need to look for it
let indexPath = collectionView.indexPathForItem(at: location)

let cell = collectionView.cellForItem(at: indexPath)
cell.frame // store this also


来源:https://stackoverflow.com/questions/51020273/get-frame-of-drag-preview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!