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

Swift实现拖动角落调整UITextView大小的方法咨询

实现UITextView拖动角落调整尺寸的Swift解决方案

核心实现思路

  • 给需要调整尺寸的UITextView添加4个角落的拖动触发视图(推荐大小24x24,支持超出UITextView边界响应点击,降低用户操作门槛)
  • 给每个拖动视图绑定UIPanGestureRecognizer手势,识别拖动位移
  • 根据拖动的角落位置动态计算UITextView的新frame,同时设置最小尺寸限制,避免控件被拉到过小无法操作
  • 额外添加选中状态逻辑,仅当文本视图被选中时显示拖动手柄和边框,契合主流照片编辑App的交互习惯

具体实现代码

第一步:自定义可调整尺寸的TextView子类

import UIKit

class ResizableTextView: UITextView {
    // 拖动手柄尺寸
    private let dragHandleSize: CGFloat = 24
    // 文本视图最小允许尺寸
    private let minTextViewSize: CGSize = CGSize(width: 60, height: 40)
    
    // 四个角落的拖动手柄
    private var topLeftHandle: UIView!
    private var topRightHandle: UIView!
    private var bottomLeftHandle: UIView!
    private var bottomRightHandle: UIView!
    
    // 记录拖动开始时的原始frame
    private var initialFrame: CGRect = .zero
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        commonSetup()
        setupDragHandles()
        setHandlesHidden(true)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonSetup()
        setupDragHandles()
        setHandlesHidden(true)
    }
    
    private func commonSetup() {
        isUserInteractionEnabled = true
        layer.borderColor = UIColor.systemBlue.cgColor
        layer.borderWidth = 0
        clipsToBounds = false
    }
    
    private func setupDragHandles() {
        // 统一创建拖动手柄
        func createHandle(action: Selector) -> UIView {
            let handle = UIView()
            handle.backgroundColor = .systemBlue
            handle.layer.cornerRadius = 4
            handle.isUserInteractionEnabled = true
            let pan = UIPanGestureRecognizer(target: self, action: action)
            handle.addGestureRecognizer(pan)
            addSubview(handle)
            return handle
        }
        
        topLeftHandle = createHandle(action: #selector(handleTopLeftDrag(_:)))
        topRightHandle = createHandle(action: #selector(handleTopRightDrag(_:)))
        bottomLeftHandle = createHandle(action: #selector(handleBottomLeftDrag(_:)))
        bottomRightHandle = createHandle(action: #selector(handleBottomRightDrag(_:)))
        
        updateHandlesPosition()
    }
    
    private func updateHandlesPosition() {
        topLeftHandle.frame = CGRect(x: -dragHandleSize/2, y: -dragHandleSize/2, width: dragHandleSize, height: dragHandleSize)
        topRightHandle.frame = CGRect(x: bounds.width - dragHandleSize/2, y: -dragHandleSize/2, width: dragHandleSize, height: dragHandleSize)
        bottomLeftHandle.frame = CGRect(x: -dragHandleSize/2, y: bounds.height - dragHandleSize/2, width: dragHandleSize, height: dragHandleSize)
        bottomRightHandle.frame = CGRect(x: bounds.width - dragHandleSize/2, y: bounds.height - dragHandleSize/2, width: dragHandleSize, height: dragHandleSize)
    }
    
    private func setHandlesHidden(_ isHidden: Bool) {
        topLeftHandle.isHidden = isHidden
        topRightHandle.isHidden = isHidden
        bottomLeftHandle.isHidden = isHidden
        bottomRightHandle.isHidden = isHidden
        layer.borderWidth = isHidden ? 0 : 1
    }
    
    // 选中状态切换手柄显示
    override var isFirstResponder: Bool {
        didSet {
            setHandlesHidden(!isFirstResponder)
        }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        updateHandlesPosition()
    }
    
    // 四个角落拖动逻辑
    @objc private func handleTopLeftDrag(_ gesture: UIPanGestureRecognizer) {
        processDrag(gesture: gesture, adjustTop: true, adjustLeft: true)
    }
    
    @objc private func handleTopRightDrag(_ gesture: UIPanGestureRecognizer) {
        processDrag(gesture: gesture, adjustTop: true, adjustLeft: false)
    }
    
    @objc private func handleBottomLeftDrag(_ gesture: UIPanGestureRecognizer) {
        processDrag(gesture: gesture, adjustTop: false, adjustLeft: true)
    }
    
    @objc private func handleBottomRightDrag(_ gesture: UIPanGestureRecognizer) {
        processDrag(gesture: gesture, adjustTop: false, adjustLeft: false)
    }
    
    private func processDrag(gesture: UIPanGestureRecognizer, adjustTop: Bool, adjustLeft: Bool) {
        let translation = gesture.translation(in: superview)
        
        if gesture.state == .began {
            initialFrame = frame
        }
        
        var newFrame = initialFrame
        
        if adjustTop {
            newFrame.origin.y = initialFrame.origin.y + translation.y
            newFrame.size.height = initialFrame.size.height - translation.y
        } else {
            newFrame.size.height = initialFrame.size.height + translation.y
        }
        
        if adjustLeft {
            newFrame.origin.x = initialFrame.origin.x + translation.x
            newFrame.size.width = initialFrame.size.width - translation.x
        } else {
            newFrame.size.width = initialFrame.size.width + translation.x
        }
        
        // 限制最小尺寸
        if newFrame.width < minTextViewSize.width {
            newFrame.size.width = minTextViewSize.width
            if adjustLeft {
                newFrame.origin.x = initialFrame.origin.x + initialFrame.width - minTextViewSize.width
            }
        }
        if newFrame.height < minTextViewSize.height {
            newFrame.size.height = minTextViewSize.height
            if adjustTop {
                newFrame.origin.y = initialFrame.origin.y + initialFrame.height - minTextViewSize.height
            }
        }
        
        frame = newFrame
    }
    
    // 新增整体拖动移动位置的方法
    func enableMoveGesture() {
        let movePan = UIPanGestureRecognizer(target: self, action: #selector(handleMoveDrag(_:)))
        addGestureRecognizer(movePan)
    }
    
    @objc private func handleMoveDrag(_ gesture: UIPanGestureRecognizer) {
        let translation = gesture.translation(in: superview)
        if gesture.state == .began {
            initialFrame = frame
        }
        frame = CGRect(x: initialFrame.origin.x + translation.x, y: initialFrame.origin.y + translation.y, width: initialFrame.width, height: initialFrame.height)
    }
}

第二步:调用示例

在你的编辑页ViewController中使用:

// 画布视图为你承载相框、照片、文本的父视图
let textView = ResizableTextView(frame: CGRect(x: 100, y: 100, width: 160, height: 80))
textView.text = "自定义文本"
textView.font = .systemFont(ofSize: 16)
textView.enableMoveGesture()
canvasView.addSubview(textView)

可选优化点

  • 给拖动手柄添加阴影效果,提升视觉辨识度
  • 调整尺寸时同步计算缩放比例,动态修改字体大小,实现文本与控件等比缩放效果
  • 支持多文本选中后批量调整尺寸

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 02:06:08