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

Swift iOS通过pan手势拖拽调整UITextView/UIView宽度及旋转适配问题

解决方案

核心问题根源

你当前实现的失效原因主要有三个:

  • 全局持有单个myView变量,无法支持多视图批量添加和独立手势管理
  • 当UIView应用了旋转等非默认transform后,直接修改frame属性的计算结果不符合预期:此时frame代表视图在父视图中的外观包围盒,不是视图内部坐标系的真实尺寸和位置
  • 左右圆点的拖拽计算基于全局屏幕坐标系,未适配视图旋转后的自身方向,导致拖拽方向和调整逻辑和预期不符

具体优化方案

将可调整视图封装为独立自定义类,所有手势逻辑、子视图布局都在类内部实现,避免全局变量依赖,调整尺寸时通过修改bounds和center属性替代frame,适配旋转场景。

完整实现代码

1. 自定义可调整视图类

import UIKit

class ResizableCustomView: UIView {
    // 内部子视图
    private let leftDotView = UIView()
    private let rightDotView = UIView()
    private let txtView = UITextView()
    
    // 最小宽度限制,避免拖拽太窄
    private let minWidth: CGFloat = 100
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
        setupGestures()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupSubviews() {
        backgroundColor = .yellow
        
        // 左红点配置
        leftDotView.backgroundColor = .red
        leftDotView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(leftDotView)
        NSLayoutConstraint.activate([
            leftDotView.leadingAnchor.constraint(equalTo: leadingAnchor),
            leftDotView.topAnchor.constraint(equalTo: topAnchor, constant: 30),
            leftDotView.widthAnchor.constraint(equalToConstant: 10),
            leftDotView.heightAnchor.constraint(equalToConstant: 40)
        ])
        
        // 右红点配置
        rightDotView.backgroundColor = .red
        rightDotView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(rightDotView)
        NSLayoutConstraint.activate([
            rightDotView.trailingAnchor.constraint(equalTo: trailingAnchor),
            rightDotView.topAnchor.constraint(equalTo: topAnchor, constant: 30),
            rightDotView.widthAnchor.constraint(equalToConstant: 10),
            rightDotView.heightAnchor.constraint(equalToConstant: 40)
        ])
        
        // 文本视图配置
        txtView.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
        txtView.font = .systemFont(ofSize: 15)
        txtView.isUserInteractionEnabled = false
        txtView.backgroundColor = .clear
        txtView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(txtView)
        NSLayoutConstraint.activate([
            txtView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
            txtView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
            txtView.topAnchor.constraint(equalTo: topAnchor, constant: 10),
            txtView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10)
        ])
    }
    
    private func setupGestures() {
        isUserInteractionEnabled = true
        // 视图整体拖拽手势
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
        panGesture.delegate = self
        addGestureRecognizer(panGesture)
        
        // 旋转手势
        let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotate(recognizer:)))
        rotateGesture.delegate = self
        addGestureRecognizer(rotateGesture)
        
        // 左红点拖拽手势
        let leftPan = UIPanGestureRecognizer(target: self, action: #selector(leftDotMoveGesture(_:)))
        leftPan.delegate = self
        leftDotView.addGestureRecognizer(leftPan)
        
        // 右红点拖拽手势
        let rightPan = UIPanGestureRecognizer(target: self, action: #selector(rightDotMoveGesture(_:)))
        rightPan.delegate = self
        rightDotView.addGestureRecognizer(rightPan)
    }
    
    // 整体拖拽逻辑
    @objc private func draggedView(_ sender: UIPanGestureRecognizer) {
        guard let superview = superview else { return }
        superview.bringSubviewToFront(self)
        let translation = sender.translation(in: superview)
        center = CGPoint(x: center.x + translation.x, y: center.y + translation.y)
        sender.setTranslation(.zero, in: superview)
    }
    
    // 旋转逻辑
    @objc private func handleRotate(recognizer: UIRotationGestureRecognizer) {
        transform = transform.rotated(by: recognizer.rotation)
        recognizer.rotation = 0
    }
    
    // 左红点拖拽调整宽度
    @objc private func leftDotMoveGesture(_ recognizer: UIPanGestureRecognizer) {
        guard let superview = superview else { return }
        let translation = recognizer.translation(in: superview)
        // 将全局平移转换为视图自身坐标系的平移,适配旋转方向
        let convertedTranslation = convert(translation, from: superview)
        let deltaX = convertedTranslation.x
        
        var newWidth = bounds.width - deltaX
        // 限制最小宽度
        guard newWidth >= minWidth else { return }
        
        bounds.size.width = newWidth
        // 调整center位置,保证右边缘不动
        let globalDeltaX = translation.x
        center = CGPoint(x: center.x + globalDeltaX / 2, y: center.y + translation.y / 2)
        
        recognizer.setTranslation(.zero, in: superview)
    }
    
    // 右红点拖拽调整宽度
    @objc private func rightDotMoveGesture(_ recognizer: UIPanGestureRecognizer) {
        guard let superview = superview else { return }
        let translation = recognizer.translation(in: superview)
        let convertedTranslation = convert(translation, from: superview)
        let deltaX = convertedTranslation.x
        
        var newWidth = bounds.width + deltaX
        guard newWidth >= minWidth else { return }
        
        bounds.size.width = newWidth
        let globalDeltaX = translation.x
        center = CGPoint(x: center.x + globalDeltaX / 2, y: center.y + translation.y / 2)
        
        recognizer.setTranslation(.zero, in: superview)
    }
}

extension ResizableCustomView: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

2. 视图控制器调用代码

需要添加多少个视图就调用多少次addCustomView即可:

func addCustomView() {
    let customView = ResizableCustomView(frame: CGRect(x: 100, y: 100, width: 200, height: 100))
    view.addSubview(customView)
}

效果说明

  • 无论视图旋转到任何角度,拖拽左右红点都可以沿视图自身水平方向调整宽度
  • 文本内容会随宽度变化自动换行重排
  • 支持同时添加任意数量的同类视图,每个视图独立处理自己的拖拽、旋转、调整逻辑,互不干扰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 11:39:01