MST

星途 面试题库

面试题:SwiftUI中复杂数据类型的拖拽与放置处理

假设你有一个自定义的结构体类型 `Person: Identifiable { var id: UUID; var name: String; var age: Int }`,现在需要在SwiftUI视图间实现 `Person` 实例的拖拽与放置。请阐述如何对该自定义类型进行适配以支持拖拽与放置功能,详细说明 `Draggable` 和 `Droppable` 相关协议的具体实现,以及在放置时如何确保数据的完整性和一致性,给出完整的代码示例。
39.4万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试
  1. 使 Person 适配拖拽与放置
    • Person 遵循 Codable 协议,这样可以方便地对其进行编码和解码,以便在拖拽与放置过程中传输数据。
    • 因为 Person 已经遵循 Identifiable 协议,我们主要关注编码解码相关实现。
  2. Draggable 协议实现
    • 在SwiftUI中,我们使用 DragGesture 相关功能,通过设置 onDrag 修饰符来实现拖拽。
    • 对于 Person 类型,我们将其编码为 Data 以便在拖拽中传递。
  3. Droppable 协议实现
    • 使用 DropDelegate 协议来处理放置操作。
    • 在放置时,将接收到的 Data 解码回 Person 实例,并进行数据完整性和一致性检查。
  4. 代码示例
import SwiftUI
import Foundation

struct Person: Identifiable, Codable {
    var id: UUID
    var name: String
    var age: Int
}

struct ContentView: View {
    @State private var people: [Person] = [
        Person(id: UUID(), name: "Alice", age: 30),
        Person(id: UUID(), name: "Bob", age: 25)
    ]
    @State private var droppedPerson: Person? = nil

    var body: some View {
        VStack {
            List(people) { person in
                Text(person.name)
                   .onDrag {
                        let encoder = JSONEncoder()
                        if let data = try? encoder.encode(person) {
                            return NSItemProvider(object: data as NSData)
                        }
                        return NSItemProvider()
                    }
            }
           .frame(width: 200)

            DropZone()
               .onDrop(of: [.json], delegate: DropDelegate())
        }
    }

    struct DropZone: View {
        var body: some View {
            Rectangle()
               .fill(Color.gray.opacity(0.2))
               .frame(width: 200, height: 200)
               .overlay(
                    Text(droppedPerson == nil? "Drop here" : "Dropped: \(droppedPerson!.name)")
                       .foregroundColor(.black)
                )
        }
    }

    class DropDelegate: NSObject, DropDelegate {
        func performDrop(info: DropInfo) -> Bool {
            guard let item = info.itemProviders(for: [.json]).first else {
                return false
            }
            item.loadObject(ofClass: NSData.self) { data, error in
                guard let data = data as? Data, error == nil else {
                    return
                }
                let decoder = JSONDecoder()
                if let person = try? decoder.decode(Person.self, from: data) {
                    // 数据完整性和一致性检查示例:检查年龄是否为正数
                    if person.age > 0 {
                        self.droppedPerson = person
                    } else {
                        print("Invalid age, data not consistent")
                    }
                }
            }
            return true
        }
    }
}

上述代码实现了以下功能:

  • Person 结构体遵循 IdentifiableCodable 协议。
  • ListView 中,每个 Person 实例可以被拖拽,通过 onDragPerson 编码为 Data 并提供给 NSItemProvider
  • DropZone 视图接收拖拽的数据,DropDelegate 中的 performDrop 方法负责将接收到的 Data 解码回 Person,并进行简单的数据完整性和一致性检查(这里是检查年龄是否为正数)。