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

Swift日记App开发:在ViewController中实现可拖拽缩放的文本图片添加功能

Hey there! Let’s break down how you can build this diary app feature that mimics Instagram Stories’ draggable, scalable elements. I’ll cover both a single ViewController approach and how to adapt it for Table/Collection View if that fits your flow better.

1. Core Setup: Add Action Buttons to Your ViewController

First, start by adding your two buttons (“Add Text” and “Add Image”) to your ViewController. You can do this programmatically or via Interface Builder—here’s a quick programmatic example in Swift:

class DiaryViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        setupButtons()
    }
    
    private func setupButtons() {
        // Add Text Button
        let addTextBtn = UIButton(type: .system)
        addTextBtn.setTitle("Add Text", for: .normal)
        addTextBtn.addTarget(self, action: #selector(addTextTapped), for: .touchUpInside)
        addTextBtn.frame = CGRect(x: 20, y: view.safeAreaInsets.top + 20, width: 120, height: 40)
        view.addSubview(addTextBtn)
        
        // Add Image Button
        let addImageBtn = UIButton(type: .system)
        addImageBtn.setTitle("Add Image", for: .normal)
        addImageBtn.addTarget(self, action: #selector(addImageTapped), for: .touchUpInside)
        addImageBtn.frame = CGRect(x: 150, y: view.safeAreaInsets.top + 20, width: 120, height: 40)
        view.addSubview(addImageBtn)
    }
    
    @objc private func addTextTapped() {
        // We'll implement this next
    }
    
    @objc private func addImageTapped() {
        // We'll implement this next
    }
}
2. Build a Reusable Draggable/Scalable Item Class

The key to mimicking Instagram Stories is having a reusable view that handles dragging and scaling. Create a subclass of UIView that includes gesture recognizers for pan and pinch:

class EditableContentItem: UIView {
    private var initialCenter: CGPoint!
    private var initialScale: CGFloat!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupGestures()
        backgroundColor = .clear
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupGestures()
        backgroundColor = .clear
    }
    
    private func setupGestures() {
        // Pan Gesture for dragging
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        addGestureRecognizer(panGesture)
        
        // Pinch Gesture for scaling
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
        addGestureRecognizer(pinchGesture)
    }
    
    @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
        switch gesture.state {
        case .began:
            initialCenter = center
        case .changed:
            let translation = gesture.translation(in: superview)
            center = CGPoint(x: initialCenter.x + translation.x, y: initialCenter.y + translation.y)
        default:
            break
        }
    }
    
    @objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        switch gesture.state {
        case .began:
            initialScale = transform.a
        case .changed:
            let scale = initialScale * gesture.scale
            transform = CGAffineTransform(scaleX: scale, y: scale)
        default:
            break
        }
    }
}

Now create specific subclasses for text and image:

// Text Item
class EditableTextItem: EditableContentItem {
    let textLabel: UILabel = {
        let label = UILabel()
        label.textColor = .black
        label.font = .systemFont(ofSize: 24)
        label.textAlignment = .center
        label.numberOfLines = 0
        return label
    }()
    
    init(text: String, frame: CGRect) {
        super.init(frame: frame)
        addSubview(textLabel)
        textLabel.text = text
        textLabel.frame = bounds
        textLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// Image Item
class EditableImageItem: EditableContentItem {
    let imageView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        iv.clipsToBounds = true
        return iv
    }()
    
    init(image: UIImage, frame: CGRect) {
        super.init(frame: frame)
        addSubview(imageView)
        imageView.image = image
        imageView.frame = bounds
        imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
3. Implement Button Actions to Add Content

Now hook up the button taps to create and add these items to your view:

extension DiaryViewController {
    @objc private func addTextTapped() {
        let alert = UIAlertController(title: "Add Text", message: "Enter your text:", preferredStyle: .alert)
        alert.addTextField { textField in
            textField.placeholder = "Type here..."
        }
        alert.addAction(UIAlertAction(title: "Add", style: .default) { [weak self] _ in
            guard let text = alert.textFields?.first?.text, !text.isEmpty else { return }
            // Create text item at a default position
            let textItem = EditableTextItem(text: text, frame: CGRect(x: 50, y: 200, width: 250, height: 100))
            self?.view.addSubview(textItem)
        })
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        present(alert, animated: true)
    }
    
    @objc private func addImageTapped() {
        let imagePicker = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.sourceType = .photoLibrary
        present(imagePicker, animated: true)
    }
}

// Conform to UIImagePickerController delegates
extension DiaryViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        guard let image = info[.originalImage] as? UIImage else {
            picker.dismiss(animated: true)
            return
        }
        // Create image item at a default position
        let imageItem = EditableImageItem(image: image, frame: CGRect(x: 50, y: 350, width: 200, height: 200))
        view.addSubview(imageItem)
        picker.dismiss(animated: true)
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true)
    }
}
4. Adapting to TableVC or CollectionVC

If you prefer using a Table or Collection View (e.g., if your diary has multiple entries/pages):

  • Collection View: Use a horizontal flow layout so each cell represents a single diary page. Each cell will act as the container view for your editable text/image items—just replicate the ViewController logic inside the Collection View Cell class.
  • Table View: Each row could be a diary entry, but since you want draggable/scalable elements, a Collection View is a better fit (it’s more flexible for full-page content).
Pro Tips
  • Layer Management: If users add multiple items, make sure the selected item comes to the front. You can add a tap gesture to EditableContentItem and call superview?.bringSubviewToFront(self) when tapped.
  • Saving Content: To persist the diary entries, save each item’s frame, transform, text, or image data (e.g., using Core Data or UserDefaults for simpler cases).
  • Customization: Let users change text color/font or image filters to make it more Instagram-like!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:11:20