You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI/UIKit实现支持图片及iOS 16贴纸粘贴的画布方案咨询

SwiftUI 实现方案

基础视图选型

  • 画布核心用 ZStack:底层放置白色背景作为空白画布,上层叠加所有可交互的贴纸/图片视图
  • 可拖动贴纸封装为自定义 DraggableStickerView:通过 DragGesture 绑定位置,支持自由拖拽
  • 上下文菜单用 .contextMenu 修饰符:绑定画布背景,触发后读取系统粘贴板内容

实现步骤

  1. 画布与粘贴逻辑
    • 定义 StickerItem 结构体存储贴纸的位置和内容:
      struct StickerItem: Identifiable {
          let id = UUID()
          var position: CGPoint
          var content: Any // 存储Image或PKSticker,按需处理
      }
      
    • 用 @State var stickers: [StickerItem] = [] 管理所有贴纸,给画布背景添加上下文菜单:
      ZStack {
          Color.white
              .contextMenu {
                  Button("粘贴") {
                      handlePaste()
                  }
              }
          ForEach($stickers) { $item in
              DraggableStickerView(item: $item)
          }
      }
      
    • 实现 handlePaste() 读取粘贴板内容,判断类型后添加到贴纸数组
  2. 可拖动视图实现
    • 自定义 DraggableStickerView,通过 DragGesture 更新绑定的位置:
      struct DraggableStickerView: View {
          @Binding var item: StickerItem
          
          var body: some View {
              if let image = item.content as? Image {
                  image
                      .resizable()
                      .scaledToFit()
                      .frame(width: 200, height: 200)
                      .position(item.position)
                      .gesture(
                          DragGesture()
                              .onChanged { value in
                                  item.position = value.location
                              }
                      )
              } else if let sticker = item.content as? PKSticker {
                  PKStickerViewRepresentable(sticker: sticker)
                      .frame(width: 150, height: 150)
                      .position(item.position)
                      .gesture(
                          DragGesture()
                              .onChanged { value in
                                  item.position = value.location
                              }
                      )
              }
          }
      }
      
    • 注:PKStickerView 需要通过 UIViewRepresentable 封装才能在SwiftUI中使用
  3. 粘贴板内容解析
    • 处理图片和iOS16贴纸的核心逻辑:
      func handlePaste() {
          let pasteboard = UIPasteboard.general
          // 处理图片
          if let uiImage = pasteboard.image {
              let image = Image(uiImage: uiImage)
              stickers.append(StickerItem(position: CGPoint(x: 100, y: 100), content: image))
          }
          // 处理iOS16贴纸
          else if let provider = pasteboard.itemProviders.first(where: { $0.canLoadObject(ofClass: PKSticker.self) }) {
              provider.loadObject(ofClass: PKSticker.self) { result, error in
                  guard let sticker = result as? PKSticker else { return }
                  DispatchQueue.main.async {
                      stickers.append(StickerItem(position: CGPoint(x: 100, y: 100), content: sticker))
                  }
              }
          }
      }
      
UIKit 实现方案

基础视图选型

  • 画布用 UIView 作为容器:底层添加白色背景视图,所有贴纸作为子视图叠加
  • 可拖动贴纸用自定义 DraggableStickerView 子类:通过触摸事件实现拖拽
  • 上下文菜单用 UILongPressGestureRecognizer 触发:唤起自定义菜单或系统 UIMenuController
  • iOS16贴纸直接用 PKStickerView(需导入 PhotosUI 框架)

实现步骤

  1. 画布与粘贴菜单
    • 创建画布容器 UIView,添加长按手势触发粘贴菜单:
      class CanvasViewController: UIViewController {
          let canvasView = UIView()
          var stickers: [DraggableStickerView] = []
          
          override func viewDidLoad() {
              super.viewDidLoad()
              setupCanvas()
              let longPress = UILongPressGestureRecognizer(target: self, action: #selector(showPasteMenu))
              canvasView.addGestureRecognizer(longPress)
          }
          
          func setupCanvas() {
              canvasView.backgroundColor = .white
              canvasView.frame = view.bounds
              view.addSubview(canvasView)
          }
          
          @objc func showPasteMenu() {
              let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
              alert.addAction(UIAlertAction(title: "粘贴", style: .default, handler: { [weak self] _ in
                  self?.handlePaste()
              }))
              alert.addAction(UIAlertAction(title: "取消", style: .cancel))
              present(alert, animated: true)
          }
      }
      
  2. 可拖动贴纸实现
    • 自定义 DraggableStickerView,重写触摸事件实现拖拽:
      class DraggableStickerView: UIView {
          private var initialTouchPoint: CGPoint = .zero
          private var initialFrame: CGRect = .zero
          
          override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
              super.touchesBegan(touches, with: event)
              guard let touch = touches.first else { return }
              initialTouchPoint = touch.location(in: superview)
              initialFrame = frame
              bringSubviewToFront(self)
          }
          
          override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
              super.touchesMoved(touches, with: event)
              guard let touch = touches.first else { return }
              let currentPoint = touch.location(in: superview)
              let offsetX = currentPoint.x - initialTouchPoint.x
              let offsetY = currentPoint.y - initialTouchPoint.y
              frame = CGRect(x: initialFrame.origin.x + offsetX, y: initialFrame.origin.y + offsetY, width: initialFrame.width, height: initialFrame.height)
          }
      }
      
  3. 粘贴板内容处理
    • 解析图片和贴纸并添加到画布:
      func handlePaste() {
          let pasteboard = UIPasteboard.general
          // 添加图片贴纸
          if let image = pasteboard.image {
              let stickerView = DraggableStickerView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
              let imageView = UIImageView(image: image)
              imageView.frame = stickerView.bounds
              imageView.contentMode = .scaleAspectFit
              stickerView.addSubview(imageView)
              canvasView.addSubview(stickerView)
              stickers.append(stickerView)
          }
          // 添加iOS16贴纸
          else if let provider = pasteboard.itemProviders.first(where: { $0.canLoadObject(ofClass: PKSticker.self) }) {
              provider.loadObject(ofClass: PKSticker.self) { [weak self] result, error in
                  guard let self = self, let sticker = result as? PKSticker else { return }
                  DispatchQueue.main.async {
                      let stickerView = PKStickerView(sticker: sticker)
                      stickerView.frame = CGRect(x: 100, y: 100, width: 150, height: 150)
                      let draggableView = DraggableStickerView(frame: stickerView.frame)
                      draggableView.addSubview(stickerView)
                      self.canvasView.addSubview(draggableView)
                      self.stickers.append(draggableView)
                  }
              }
          }
      }
      

内容的提问来源于stack exchange,提问作者charelf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 18:55:29