Open tilseam opened 3 years ago
I did some research. I have found several mentions of a suspected bug in NSTableView
when using .gap
dragging style. I suspect that NSOutlineView
is inheriting that bug. The problem is that when using .gap
the item
and index
passed to outlineView(_:acceptDrop:item:childIndex)
is always nil
, and 0
, so of course it goes to the first location. I'm trying to figure out a work-around, but I think it will involve hit testing the dragged location, which is an NSPoint
against subviews frames in window coordinates.
I believe I found a solution! Do a git pull
and check it out. Let me know if it works as you expect for you.
I also updated my answer on StackOverflow
Thx Chip! That works for me!
It is a brilliant idea to add a coordinate judgment, and it led me to think more deeply. I think we should make the interaction more intuitive, the coordinate judgment may be placed between validateDrop{} :
func outlineView(_ outlineView: NSOutlineView,
validateDrop info: NSDraggingInfo,
proposedItem item: Any?,
proposedChildIndex index: Int) -> NSDragOperation {
let point = outlineView.convert(info.draggingLocation, from: nil)
let firstCellFrame = outlineView.frameOfCell(atColumn: 0, row: 0)
let dragDown:Bool = outlineView.isFlipped // An indicator to determine whether it is dragged down
? (point.y < firstCellFrame.maxY ? false : true)
: (point.y >= firstCellFrame.minY ? false : true)
if (index < 0 && item == nil) ||
(item == nil && index == 0 && dragDown){
return []
}else{
outlineView.draggingDestinationFeedbackStyle = .gap
return .move
}
}
Therefore, when we dragging down any items to the bottom, there won't be any incorrect animation feedback.
Yes, I noticed that the cell animation kept the last cell in a fixed location when dragging past the bottom cell so that the cell above it would animate on top of it. Does applying the draggingLocation
in validateDrop
fix the index for acceptDrop
?
Actually animation strangeness I noticed was when moving a cell up.
I tried it with the changes to validateDrop
. It no longer accepts drops past the last cell. It places the dragged item back into its original location. Is that the behavior you want?
Actually animation strangeness I noticed was when moving a cell up.
I tried it with the changes to
validateDrop
. It no longer accepts drops past the last cell. It places the dragged item back into its original location. Is that the behavior you want?
I didn't find any problem in my program, the Finder's sidebar gives the same feedback, I would think this is natural.
If I remove my fix to acceptDrop
and add your changes to validateDrop
, this is what I get:
I get the same result if I restore my fix for acceptDrop
and keep your changes to validateDrop
. Notice how the items do not move to the end of the list. They go back to their original location.
I also notice that in my debugging output that validateDrop
is getting an index
of -2.
I think it is normal that the item to return to its original position after being dragged far from the list boundary(return index of -2), as Finder feedbacks in the same way. When you drag one item to to the bottom of the list, you can only drag a little bit below the last item in the list.
Whether it is more natural is a matter of taste. I'm trying to help you achieve the look and feel you want, not what I would prefer, but I will give you a different perspective to consider.
The .gap
dragging style makes me feel like I'm trying shoot a bird in flight. Things are moving around while I'm trying to place the item where I want. It's not that big of a deal, but I do notice my frustration rises when views do that.
With the .regular
style, the items stay where they are. My brain doesn't have to engage in dynamic targeting, and the insertion line very clearly says "If you drop now, this is where it will go." It's visually less flashy, but I think more effective UI design.
I think the Finder side bar is an NSTableView
. If you look at the main Finder view that shows folder contents in list mode, it is hierarchical so must be an NSOutlineView
. It does not use .gap
. Perhaps we have discovered why.
Speaking as a user of applications, if I drag an item below the last item in a list, as long as I'm still in the list view, I expect it to go below the last item. The Finder side bar does that, except that you have different list views. When you drag too far down you leave your favorite items and move into the view for labels... that's a different view, so the drop is not valid.
Nonetheless, it is your app, so the decision is yours, not mine. If the change gives you the behavior you want then we have succeeded.
Thank you sincerely! I've got what I want! This problem has entangled me for weeks, I am really not sure whether this is a "bug" or not, or just I set sth wrong.
Finder is a NSTableView. And I think there may be a reason why Apple left this "bug"——Apple may discourage to use .gap style with NSOutlinView.
In this case, it seems to be Apple's bug. I would guess that the only reason Apple has not fixed it is because not enough developers have complained, so it has not been a high priority. And if all the developers who do complain find a work-around, there is less pressure on Apple to fix it.
NSTableView
and NSOutlineView
are very old classes. They go way back to NeXTStep (which is where the NS
prefix comes from). I think .gap
was retrofitted into it sometime in the past 10 years.
lol...As a Newbie who has been in contact with programming/swift for less than half a year, I dare not think that I will reach the place of programming language bugs. Besides, NSOutlineView should be a very basic component... Although during this period of time, I have suffered a lot from the legacy problems of OC, Swift 3 or various versions of the language.
Before I started writing programs, I thought it has been an era of NO CODE !! And implementing some basic requirements should be as simple as building LEGOs...I really never thought about facing sth. like this...
It doesn't help that Swift is a moving target, and now there is SwiftUI to learn. I'm a little concerned that Swift is starting to enable too much "magic." In particular I'm thinking of result builders. SwiftUI would not exist without them, but the compiler has to generate a lot code for you that's hard to reason about. When it goes well, it results in a beautiful declarative style. But when you get it wrong, often the errors you get aren't even on the line of code causing the error, which makes it hard to figure out what you did wrong. And you can get unexpected behavior without a good indication of why.
I was just thinking about when I first noticed the .gap
dragging style. It was some time after the iPhone came out. UITableView
supports it, so my guess is that they added it to NSTableView
to have an equivalent behavior on macOS. The problem is that NSOutlineView
inherits from NSTableView
and I don't think there is anything like NSOutlineView
in UIKit. Hierarchical structures usually involve navigating to a new view on iOS.
Lol..People keep trying to come up with something like LEGO-style programming, but they always fall short of the goal. We do get some new libraries that make things easier. Imagine if there were no AppKit. You'd have to process events manually. In fact that's what we had to do back in classic macOS days (before OS X)... and do it in a lot less RAM with less disk space.
Hi,Chip I'm the guy from stackoverflow, I've tested your code,I hope to explain my thoughts more clearly: I add one line in your code:
It will be shown as below:
And what confuses me most is that when dragging "Item4" down,it will be reorganized to the first row of the list. The same issue if I drag any other items.