返回博客首页
← 所有文章

如何在 iOS 上启用拖放…甚至从应用程序外部!

2023 年 8 月 21 日 — 作者:技术指导委员会 (TSC)

在 iOS 上启用拖放功能可以有效连接理想的用户体验流程。让我们看看在 NativeScript 应用程序中启用此行为有多么简单。

此处提供的详细信息的基础均直接取自官方 iOS 平台文档,Apple 开发者文档

您现在甚至可以在 StackBlitz 上亲身体验: 👉 https://stackblitz.com/edit/nativescript-drag-n-drop-ios

设置一个拖放区域工具

让我们设置一个工具,它可以接收任何 View 并为我们连接拖放交互。

import { View } from '@nativescript/core'

export function createDropZone(
  view: View,
  callback: (image: UIImage) => void
) {
  // 1. Create a delegate with our callback reference
  const dropDelegate = DropDelegate.new() as DropDelegate
  dropDelegate.callback = callback

  // 2. Add desired interaction configured with the delegate to our View
  const drop = UIDropInteraction.alloc().initWithDelegate(dropDelegate)
  (view.ios as UIView).addInteraction(drop)
}

第一个参数允许我们传入任何 NativeScript View 实例。第二个参数允许我们传入一个回调,其中包含拖放到我们的 View 上的图像。

根据Apple 开发者文档,我们想要设置一个UIDropInteractionDelegate,其中包含概述的方法,并创建一个UIDropInteraction 并绑定到该委托。

使用回调创建委托

我们可以如下在 TypeScript 中直接设置这样的委托

@NativeClass()
export class DropDelegate
  extends NSObject
  implements UIDropInteractionDelegate
{
  // This tells iOS our object conforms to UIDropInteractionDelegate
  static ObjCProtocols = [UIDropInteractionDelegate]

  // A reference to our callback to communicate when the drop occurred
  callback: (image: UIImage) => void

  // iOS delegate implementation details
  dropInteractionCanHandleSession(
    interaction: UIDropInteraction,
    session: UIDropSession
  ): boolean {
    return session.canLoadObjectsOfClass(UIImage.class())
  }

  dropInteractionSessionDidUpdate(
    interaction: UIDropInteraction,
    session: UIDropSession
  ): UIDropProposal {
    return UIDropProposal.alloc().initWithDropOperation(UIDropOperation.Copy)
  }

  dropInteractionPerformDrop(
    interaction: UIDropInteraction,
    session: UIDropSession
  ): void {
    session.loadObjectsOfClassCompletion(UIImage.class(), (imageItems) => {
      const image = imageItems.objectAtIndex(0)
      this.callback(image as UIImage)
    });
  }
}

让我们分解一下此实现中发生的事情

  1. 我们使用 @NativeClass() 装饰类,因为我们正在扩展一个平台原生类,NSObject
  2. 我们使用 implements UIDropInteractionDelegate 为所有可从该接口使用的方法提供丰富的 TypeScript 智能感知。
  3. 我们声明 static ObjCProtocols = [UIDropInteractionDelegate] 以告知 iOS 我们的对象符合UIDropInteractionDelegate
  4. 我们声明 callback: (image: UIImage) => void 主要用于友好的 TypeScript 用法,以引用我们的回调并在拖放发生时进行通信。
  5. 我们根据官方平台Apple 开发者文档 中的描述实现这些方法。

我们的工具的功能

设置好这些细节后,我们的 JavaScript 工具函数现在可以为任何所需的 View 创建委托,以向其添加拖放交互。

// 1. Create a delegate with our callback reference
const dropDelegate = DropDelegate.new() as DropDelegate
dropDelegate.callback = callback

// 2. Add desired interaction configured with the delegate to our View
const drop = UIDropInteraction.alloc().initWithDelegate(dropDelegate)
(view.ios as UIView).addInteraction(drop)

连接一个 View 🎉

我们可以获取任何 View 并使用它的 loaded 事件来设置连接。

<GridLayout (loaded)="loadedDropZone($event)">
    <Image [src]="droppedImage" />
</GridLayout>

loaded 事件触发时,它会将 View 引用传递给我们的工具

function loadedDropZone(event) {
    createDropZone(event.object, image => {
        this.droppedImage = image
    })
}

使用任何你喜欢的风格及其自己的绑定语法,这将在任何条件下都能正常工作。

在 StackBlitz 上亲身体验

令人惊奇的是,这一切现在都在 StackBlitz 上运行! 👉 https://stackblitz.com/edit/nativescript-drag-n-drop-ios