惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Application and Cybersecurity Blog
Application and Cybersecurity Blog
B
Blog RSS Feed
M
MIT News - Artificial intelligence
爱范儿
爱范儿
V
V2EX
雷峰网
雷峰网
D
Docker
美团技术团队
N
Netflix TechBlog - Medium
C
Cisco Blogs
T
Threatpost
K
Kaspersky official blog
P
Privacy International News Feed
W
WeLiveSecurity
T
Tor Project blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
F
Full Disclosure
Forbes - Security
Forbes - Security
V
Vulnerabilities – Threatpost
I
Intezer
有赞技术团队
有赞技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Google Online Security Blog
Google Online Security Blog
The Register - Security
The Register - Security
GbyAI
GbyAI
Security Archives - TechRepublic
Security Archives - TechRepublic
Help Net Security
Help Net Security
人人都是产品经理
人人都是产品经理
SecWiki News
SecWiki News
Cyberwarzone
Cyberwarzone
Vercel News
Vercel News
罗磊的独立博客
The Hacker News
The Hacker News
腾讯CDC
S
Security @ Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
WordPress大学
WordPress大学
Recorded Future
Recorded Future
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
小众软件
小众软件
Hacker News: Ask HN
Hacker News: Ask HN
P
Proofpoint News Feed
TaoSecurity Blog
TaoSecurity Blog
IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security Affairs
C
Check Point Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

Peter Steinberger

OpenClaw, OpenAI and the future | Peter Steinberger Shipping at Inference-Speed | Peter Steinberger The Signature Flicker | Peter Steinberger Just Talk To It - the no-bs Way of Agentic Engineering | Peter Steinberger Claude Code Anonymous | Peter Steinberger Live Coding Session: Building Arena | Peter Steinberger My Current AI Dev Workflow | Peter Steinberger Essential Reading for Agentic Engineers - August 2025 | Peter Steinberger Just One More Prompt | Peter Steinberger Poltergeist: The Ghost That Keeps Your Builds Fresh | Peter Steinberger Don't read this Startup Slop | Peter Steinberger Essential Reading for Agentic Engineers - July 2025 | Peter Steinberger Self-Hosting AI Models After Claude's Usage Limits | Peter Steinberger Logging Privacy Shenanigans | Peter Steinberger VibeTunnel's first AI-anniversary | Peter Steinberger Making AppleScript Work in macOS CLI Tools: The Undocumented Parts | Peter Steinberger Peekaboo 2.0 – Free the CLI from its MCP shackles | Peter Steinberger Command your Claude Code Army, Reloaded | Peter Steinberger Essential Reading for Agentic Engineers | Peter Steinberger Slot Machines for Programmers: How Peter Builds Apps 20x Faster with AI | Peter Steinberger My AI Workflow for Understanding Any Codebase | Peter Steinberger stats.store: Privacy-First Sparkle Analytics | Peter Steinberger Showing Settings from macOS Menu Bar Items: A 5-Hour Journey | Peter Steinberger VibeTunnel: Turn Any Browser into Your Mac's Terminal | Peter Steinberger Vibe Meter 2.0: Calculating Claude Code Usage with Token Counting | Peter Steinberger llm.codes: Make Apple Docs AI-Readable | Peter Steinberger Automatic Observation Tracking in UIKit and AppKit: The Feature Apple Forgot to Mention | Peter Steinberger Peekaboo MCP – lightning-fast macOS screenshots for AI agents | Peter Steinberger Migrating 700+ Tests to Swift Testing: A Real-World Experience | Peter Steinberger Commanding Your Claude Code Army | Peter Steinberger Code Signing and Notarization: Sparkle and Tears | Peter Steinberger Vibe Meter: Monitor Your AI Costs | Peter Steinberger Claude Code is My Computer | Peter Steinberger Stop Over-thinking AI Subscriptions | Peter Steinberger Introducing Demark: HTML in. MD out. Blink-fast. | Peter Steinberger The Future of Vibe Coding: Building with AI, Live and Unfiltered | Peter Steinberger MCP Best Practices | Peter Steinberger Finding My Spark Again | Peter Steinberger Top-Level Menu Visibility in SwiftUI for macOS | Peter Steinberger Fixing keyboardShortcut in SwiftUI | Peter Steinberger Supporting Both Tap and Long Press on a Button in SwiftUI | Peter Steinberger On Using Apple Silicon Mac Mini for Continuous Integration | Peter Steinberger Apple Silicon M1: A Developer's Perspective | Peter Steinberger Gardening Your Twitter: Curating Your Timeline | Peter Steinberger Gardening Your Twitter: Growing Your Followers | Peter Steinberger Forbidden Controls in Catalyst: Optimize Interface for Mac | Peter Steinberger Disabling Keyboard Avoidance in SwiftUI's UIHostingController | Peter Steinberger The State of SwiftUI | Peter Steinberger Logging in Swift | Peter Steinberger Building with Swift Trunk Development Snapshots | Peter Steinberger Calling Super at Runtime in Swift | Peter Steinberger zld — A Faster Version of Apple's Linker | Peter Steinberger How to Fix LLDB: Couldn't IRGen Expression | Peter Steinberger Updating macOS on a Hackintosh | Peter Steinberger InterposeKit — Elegant Swizzling in Swift | Peter Steinberger The Great Mac Catalyst Text Input Crash Hunt | Peter Steinberger Jailbreaking for iOS Developers | Peter Steinberger Network Kernel Core Dump | Peter Steinberger How to macOS Core Dump | Peter Steinberger Kernel Panics and Surprise boot-args | Peter Steinberger The LG UltraFine 5K, kernel_task, and Me | Peter Steinberger Let's Try This Again | Peter Steinberger How We Work at PSPDFKit | Peter Steinberger Swizzling in Swift | Peter Steinberger WWDC for First-Timers, 2019 Edition | Peter Steinberger Marzipan: Porting iOS Apps to the Mac | Peter Steinberger How to Use Slack and Not Go Crazy | Peter Steinberger Hardcore Debugging - Heavy Weapons for Hard Bugs | Peter Steinberger Binary Frameworks in Swift | Peter Steinberger Even Swiftier Objective-C | Peter Steinberger The Case for Deprecating UITableView | Peter Steinberger Running tests with Clang Address Sanitizer | Peter Steinberger UI testing on iOS, without busy waiting | Peter Steinberger Hiring a distributed team | Peter Steinberger Writing Good Bug Reports | Peter Steinberger Real-time collaboration, Apple, and you | Peter Steinberger Converting Xcode Test Runs to JUnit, the Fast Way | Peter Steinberger Efficient iOS Version Checking | Peter Steinberger Investigating Thread Safety of UIImage | Peter Steinberger Swifty Objective-C | Peter Steinberger Running UI Tests on iOS With Ludicrous Speed | Peter Steinberger A Pragmatic Approach to Cross-Platform | Peter Steinberger Surprises with Swift Extensions | Peter Steinberger Using ccache for Fun and Profit | Peter Steinberger UITableViewController designated initializer woes | Peter Steinberger Researching ResearchKit | Peter Steinberger The curious case of rotation with multiple windows on iOS 8 | Peter Steinberger UIKit Debug Mode | Peter Steinberger Retrofitting containsString: on iOS 7 | Peter Steinberger A Story About Swizzling "the Right Way™" and Touch Forwarding | Peter Steinberger Hacking with Aspects | Peter Steinberger Fixing UITextView On iOS 7 | Peter Steinberger Fixing What Apple Doesn't | Peter Steinberger How To Inspect The View Hierarchy Of Third-Party Apps | Peter Steinberger Fixing UISearchDisplayController On iOS 7 | Peter Steinberger Smart Proxy Delegation | Peter Steinberger Adding Keyboard Shortcuts To UIAlertView | Peter Steinberger How To Center Content Within UIScrollView | Peter Steinberger UIAppearance for Custom Views | Peter Steinberger Hacking Block Support Into UIMenuItem | Peter Steinberger
Challenges of Adopting Drag and Drop | Peter Steinberger
Peter Steinberger · 2018-11-07 · via Peter Steinberger

Earlier this year, we released PSPDFKit for iOS (7.7) and PDF Viewer (3.0). In both of these releases, we improved our Document Editor to support drag and drop for iPads on iOS 11 and above.

Although we’ve written about drag and drop before, this time I want to focus a little bit on it within the context of editing PDF files: what worked and didn’t work for us, the challenges we faced, and what we’d like for the drag-and-drop API to support in the future.

UICollectionView

Drag-and-drop interactions are available to any view that implements the UIDragInteractionDelegate/UIDropInteractionDelegate protocols. However, there are some particular cases in which our friends at Apple working on UIKit noticed an opportunity to provide specialized implementations of the drag-and-drop interactions, such as UITextView, UITableView, and UICollectionView.

Luckily for us, UICollectionView was already the backbone of our Document Editor, so we thought adding drag-and-drop support would be a breeze: a couple lines of code and we’d be done.

Although the drag-and-drop API is well documented, expressive, and generally great to work with, adapting it to a use case in which some of the API assumptions were not always true was a fun and challenging process.

What Does a Drag Item Represent?

PDF is a really versatile format, in that, in addition to allowing the user to embed actual functioning programs (via JavaScript) within a document, plus audio and video, a document can also have multiple pages!

Ironically enough, the JavaScript and media bits of the document are not the difficult part of this story.

A PDF document being able to have multiple pages posed a challenge for this feature’s implementation because the drag-and-drop API doesn’t know how to reason about a single item in a drop session representing multiple “information units” with respect to what’s being dragged.

Conforming with UICollectionViewDragDelegate, you can provide an array of items to include in the session that’s currently beginning and then add more via another delegate method:

- (NSArray<UIDragItem *> *)collectionView:(UICollectionView *)collectionView itemsForBeginningDragSession:(id<UIDragSession>)session atIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)) {
    return @[[self dragItemForDocumentPageAtIndexPath:indexPath]];
}

- (NSArray<UIDragItem *> *)collectionView:(UICollectionView *)collectionView itemsForAddingToDragSession:(id<UIDragSession>)session atIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(11.0)) {
    return @[[self dragItemForDocumentPageAtIndexPath:indexPath]];
}

The method in the above calls, - dragItemForDocumentPageAtIndexPath:, takes care of registering the appropriate file representations with the NSItemProvider instance that’s ultimately used to instantiate a single UIDragItem instance that we return. Due to how the UIKit API is designed, we need to always return an array, even if it only contains one item.

Here’s how the item drop is performed on the collection view:

- (void)collectionView:(UICollectionView *)collectionView performDropWithCoordinator:(id<UICollectionViewDropCoordinator>)coordinator API_AVAILABLE(ios(11.0)) {
    foreach(item, coordinator.items) {
        let itemProvider = item.dragItem.itemProvider;
        if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePDF]) {
            [self handlePDFDropForDropItem:item withCoordinator:coordinator];
        } else if ([itemProvider canLoadObjectOfClass:UIImage.class]) {
            [self handleImageDropForDropItem:item withCoordinator:coordinator];
        }
    }
}

The UICollectionViewDropCoordinator object that’s passed to the delegate when a drop interaction is performed within a collection view contains information about the drop. The .items property is an array of UICollectionViewDropItem instances, each of which contains a property, dragItem, that points to the original item included in the session (you can see that there’s really complicated stuff going on behind the scenes).

So far, the API seems really sensible, but it breaks apart when you consider our use case, where an item in a drop session does not necessarily represent an item to be inserted in the collection view. A user can drag a single-page PDF file from Files and drop it into our Document Editor and everything behaves as expected. But remember how PDFs can contain multiple pages as well? In such a case, the one-to-one relation doesn’t work.

When solving the importing part of this issue, we opted to always delete the placeholder inserted on the collection view when performing a drop via the UICollectionViewDropPlaceholderContext as soon as we’re done loading the item from the NSItemProvider, and to instead just insert the appropriate items into the collection view manually.

Be advised: If you call -[id<UICollectionViewDropPlaceholderContext> commitInsertionWithDataSourceUpdates:] and the collection view ends up with more items than expected, UICollectionView will crash internally. When importing PDFs, always deleting the placeholder works better because then you can manually update the collection view to your liking and not be tied to what the placeholder expects the result to be.

This is not an issue when importing pictures from Photos.app, for example, since at that point it does make sense that one drag item represents one picture. No need to worry in that case.

The fact that we can’t have a single drag item represent multiple information units when reasoning about exporting pages out of our Document Editor posed a challenge as well. Dragging a single page out should generate a single-page PDF for the application accepting the drop. But since we’re unable to tell UIKit that a single UIDragItem can actually represent multiple information units, dragging multiple pages out of our editor resulted in a single-page PDF being generated for each page exported, which is not ideal.

We tried to circumvent this limitation with a little bit of hackery on our side. Our idea relied on the fact that returning UIDragItem instances created with “empty” NSItemProviders made the collection view behave as if the session was still valid (which it technically still is). Unfortunately, the drag source application has no visibility of what the drop destination application decides to do. This means we can’t really rely on this feature working, since every application our user tries to drop stuff on can behave differently, and we have no way to influence the end result once the drag session has left the host app.

For example, in the source app, if the drag delegate methods return a list of UIDragItems but at least one of them was created with an “empty” NSItemProvider and a drop is attempted in Files.app, none of the valid drag items in the session will be dropped either, effectively behaving as if the drop operation was UIDropOperationCancel. This is a decision made by Files.app internally.

Photos.app does not behave like this. If one of the providers associated with the drag items being dropped is not able to provide information about the item being dropped, Photos.app will still import the “valid” items and display a message, “Some Items Not Imported.” Personally, I think this is a nice compromise (way better than canceling the drop operation altogether). It would, however, be even nicer if the source application could play a role in what the end result looks like.

We’ve filed a couple of enhancement requests with Apple for these issues (41790438, 43009124). Please feel free to duplicate these radars if you think the enhancements would be useful for you.

Reordering Multiple Items

This section is really small because there’s not much we can do or say about this. Drag and drop just does not support UIDropOperationMove operations when the drop session contains multiple items.

We also filed an enhancement request for this feature (42068699), and you can duplicate it if you’d find it useful too.

Multi-Document Environment

As part of PSPDFKit for iOS, we offer the ability to display multiple documents in a tabbed environment (à la Safari) via the PSPDFTabbedViewController class. On PDF Viewer, this is one of our marquee features.

We thought it would be pretty awesome to allow our users to start a drag from one document and drop the pages into another one of the documents opened in that multi-document environment, so we tried implementing it, and we got pretty close to shipping it.

But then we hit a wall, and after a long time debugging the issue and trying to replicate a weird crash within UIKit (__UIDragSnappingFeedbackGenerator), we decided to take a step back and really think about what we were doing (or at least trying to do).

As our CEO Peter Steinberger mentioned in his guest appearance on the Swift by Sundell podcast, we discovered that our approach to the multi-document display environment is simply not compatible with what the drag-and-drop API expects.

Rendering a PDF is a really complicated task and can take up a lot of memory. Within PSPDFKit, we do a lot of heavy lifting to make sure you can display a PDF in your application with only a couple lines of code — and much of that heavy lifting has to do with keeping our memory profile as low as possible.

One of the strategies we use to keep our memory profile low is exemplified in our implementation of PSPDFTabbedViewController: Internally, we don’t maintain a view hierarchy per document, but rather we reuse the entire scaffold and only refresh the contents when the document tab changes (just how you can call reloadData on a UITableView).

Switching documents for us means we need to reload the internal collection view that displays the list of pages for the active document, which does not play well if you’re in the middle of a drag session. It basically “ends the party” and is effectively undefined behavior territory, which is really frustrating for us because it otherwise works fine three or four times before crashing.

This leaves us with the following options if we want to support this feature in a reliable manner:

  • Rewriting our presenting architecture to make reusing components easier without the need of a full reload
  • Maintaining a collection view per document
  • Writing our own UICollectionView replacement class that allows us to manually track the dragging session so that reloading items being presented is not an issue

We’re still debating the correct approach to take here, since this feature would be a really nice addition to our already super-capable Document Editor. In the meantime, here’s a radar you can duplicate: 42180829.

Conclusion

Sadly, not much of UIKit changed with iOS 12, so here’s hoping that iOS 13 expands the capabilities of drag and drop, as it is a really powerful, well designed API. As you can see, we don’t really have any complaints, but rather ideas of how it could be improved to add more value to the iOS ecosystem.

At PSPDFKit, we know that a great API is always a delight to work with, so we are really happy with how adding drag-and-drop support to our Document Editor turned out, even if we had to restrict some of the features we had planned.

You can play with the Document Editor drag-and-drop support if you get PDF Viewer Pro, and you can customize what can be done with it if you’re an SDK user (you can download a free trial of our SDK to play around with it as well).