如何通过拖动边缘来调整UIView的大小?

28
在我的iPad应用中,我希望用户能够通过拖动视图边缘来调整UIView的大小。 我将使用iOS 5 SDK,那么最干净的方法是什么? 是否有其他方法可以在不涉及touchesBegan,touchesMoved等事件的情况下实现这一点?
6个回答

57

您可以通过检查触摸起始点来实现此操作。如果它碰到你的四个角之一,你可以根据该触摸起始点和当前触摸点之间的距离进行缩放调整。(如果触摸起始点没有碰到一个角,我们只是移动视图而不是调整大小。)

定义可拖动角的大小。

CGFloat kResizeThumbSize = 45.0f;

将这些实例变量添加到您的类中,以跟踪触摸状态和我们正在调整大小的方向。

@interface MY_CLASS_NAME : UIView {
    BOOL isResizingLR;
    BOOL isResizingUL;
    BOOL isResizingUR;
    BOOL isResizingLL;
    CGPoint touchStart;
}

处理触摸开始/变化事件。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [[event allTouches] anyObject];
    touchStart = [[touches anyObject] locationInView:self];
    isResizingLR = (self.bounds.size.width - touchStart.x < kResizeThumbSize && self.bounds.size.height - touchStart.y < kResizeThumbSize);
    isResizingUL = (touchStart.x <kResizeThumbSize && touchStart.y <kResizeThumbSize);
    isResizingUR = (self.bounds.size.width-touchStart.x < kResizeThumbSize && touchStart.y<kResizeThumbSize);
    isResizingLL = (touchStart.x <kResizeThumbSize && self.bounds.size.height -touchStart.y <kResizeThumbSize);
}

 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint touchPoint = [[touches anyObject] locationInView:self];
    CGPoint previous = [[touches anyObject] previousLocationInView:self];

    CGFloat deltaWidth = touchPoint.x - previous.x;
    CGFloat deltaHeight = touchPoint.y - previous.y;

    // get the frame values so we can calculate changes below
    CGFloat x = self.frame.origin.x;
    CGFloat y = self.frame.origin.y;
    CGFloat width = self.frame.size.width;
    CGFloat height = self.frame.size.height;

    if (isResizingLR) {
        self.frame = CGRectMake(x, y, touchPoint.x+deltaWidth, touchPoint.y+deltaWidth);
    } else if (isResizingUL) {
        self.frame = CGRectMake(x+deltaWidth, y+deltaHeight, width-deltaWidth, height-deltaHeight);
    } else if (isResizingUR) {
        self.frame = CGRectMake(x, y+deltaHeight, width+deltaWidth, height-deltaHeight);      
    } else if (isResizingLL) {
        self.frame = CGRectMake(x+deltaWidth, y, width-deltaWidth, height+deltaHeight);   
    } else {
        // not dragging from a corner -- move the view
        self.center = CGPointMake(self.center.x + touchPoint.x - touchStart.x,
            self.center.y + touchPoint.y - touchStart.y);
    }
} 

1
太棒了,谢谢!这比其他任何东西都更有意义!!但是我认为第一个if (isResizingLR) {需要是y + deltaHeight而不是delta width-请告诉我这是否正确?谢谢。 - devjme

17

我使用了enum更新了上面的代码。

class ResizableView: UIView {

    enum Edge {
        case topLeft, topRight, bottomLeft, bottomRight, none
    }

    static var edgeSize: CGFloat = 44.0
    private typealias `Self` = ResizableView

    var currentEdge: Edge = .none
    var touchStart = CGPoint.zero

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {

            touchStart = touch.location(in: self)

            currentEdge = {
                if self.bounds.size.width - touchStart.x < Self.edgeSize && self.bounds.size.height - touchStart.y < Self.edgeSize {
                    return .bottomRight
                } else if touchStart.x < Self.edgeSize && touchStart.y < Self.edgeSize {
                    return .topLeft
                } else if self.bounds.size.width-touchStart.x < Self.edgeSize && touchStart.y < Self.edgeSize {
                    return .topRight
                } else if touchStart.x < Self.edgeSize && self.bounds.size.height - touchStart.y < Self.edgeSize {
                    return .bottomLeft
                }
                return .none
            }()
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let currentPoint = touch.location(in: self)
            let previous = touch.previousLocation(in: self)

            let originX = self.frame.origin.x
            let originY = self.frame.origin.y
            let width = self.frame.size.width
            let height = self.frame.size.height

            let deltaWidth = currentPoint.x - previous.x
            let deltaHeight = currentPoint.y - previous.y

            switch currentEdge {
            case .topLeft:
                self.frame = CGRect(x: originX + deltaWidth, y: originY + deltaHeight, width: width - deltaWidth, height: height - deltaHeight)
            case .topRight:
                self.frame = CGRect(x: originX, y: originY + deltaHeight, width: width + deltaWidth, height: height - deltaHeight)
            case .bottomRight:
                self.frame = CGRect(x: originX, y: originY, width: width + deltaWidth, height: height + deltaWidth)
            case .bottomLeft:
                self.frame = CGRect(x: originX + deltaWidth, y: originY, width: width - deltaWidth, height: height + deltaHeight)
            default:
                // Moving
                self.center = CGPoint(x: self.center.x + currentPoint.x - touchStart.x,
                                      y: self.center.y + currentPoint.y - touchStart.y)
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        currentEdge = .none
    }
}

currentEdge 保存用户触摸位置的状态。


请问您能否更新您的答案,通过放置布尔检查来在从任何边缘拖动时保持纵横比?那将是一个很好的补充。 - Rashesh Bosamiya

14

我猜测你的UI使用了视图边缘上的某种手柄,并且将一个简单的UIPanGestureRecognizer附加到这些手柄控件上能够使整个问题变得非常容易。

在手势识别器的动作方法中,只需要获取与正在调整大小的视图相关的-translationInView:,在手势识别器的状态为UIGestureRecognizerStateBegan时保存原始帧,而在状态为UIGestureRecognizerStateChanged时不断地调整视图的帧即可。


1
我没有在侧边放置任何手柄(因为我需要遵循我们设计师的UI设计),但我想我可以放置不可见的视图或类似的东西来附加手势识别器。 - aslı

12

这是一个使用AutoLayout和Constraint-AnimationSwift 4.2 解决方案。

由于在使用AutoLayout时建议动画约束而不是实际矩形的大小,因此该解决方案正是如此。

其他功能:

  • 当不在边缘时仅调整一侧大小。
  • 在中间触摸时移动整个矩形。

在此处查看其视频演示: https://imgur.com/a/CrkARLi

导入是将约束作为输出连接以进行动画处理。

将约束作为输出连接以进行动画处理

import UIKit

class ViewController: UIViewController {


@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var rightConstraint: NSLayoutConstraint!
@IBOutlet weak var leftConstraint: NSLayoutConstraint!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
@IBOutlet weak var rect: UIView!

struct ResizeRect{
    var topTouch = false
    var leftTouch = false
    var rightTouch = false
    var bottomTouch = false
    var middelTouch = false
}

var touchStart = CGPoint.zero
var proxyFactor = CGFloat(10)
var resizeRect = ResizeRect()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first{

        let touchStart = touch.location(in: self.view)
        print(touchStart)

        resizeRect.topTouch = false
        resizeRect.leftTouch = false
        resizeRect.rightTouch = false
        resizeRect.bottomTouch = false
        resizeRect.middelTouch = false

        if  touchStart.y > rect.frame.minY + (proxyFactor*2) &&  touchStart.y < rect.frame.maxY - (proxyFactor*2) &&  touchStart.x > rect.frame.minX + (proxyFactor*2) &&  touchStart.x < rect.frame.maxX - (proxyFactor*2){
            resizeRect.middelTouch = true
            print("middle")
            return
        }

        if touchStart.y > rect.frame.maxY - proxyFactor &&  touchStart.y < rect.frame.maxY + proxyFactor {
            resizeRect.bottomTouch = true
            print("bottom")
        }

        if touchStart.x > rect.frame.maxX - proxyFactor && touchStart.x < rect.frame.maxX + proxyFactor {
            resizeRect.rightTouch = true
            print("right")
        }

        if touchStart.x > rect.frame.minX - proxyFactor &&  touchStart.x < rect.frame.minX + proxyFactor {
            resizeRect.leftTouch = true
            print("left")
        }

        if touchStart.y > rect.frame.minY - proxyFactor &&  touchStart.y < rect.frame.minY + proxyFactor {
            resizeRect.topTouch = true
            print("top")
        }

    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first{
        let currentTouchPoint = touch.location(in: self.view)
        let previousTouchPoint = touch.previousLocation(in: self.view)

        let deltaX = currentTouchPoint.x - previousTouchPoint.x
        let deltaY = currentTouchPoint.y - previousTouchPoint.y


        if resizeRect.middelTouch{
            topConstraint.constant += deltaY
            leftConstraint.constant += deltaX
            rightConstraint.constant -= deltaX
            bottomConstraint.constant -= deltaY
        }

        if resizeRect.topTouch {
            topConstraint.constant += deltaY
        }

        if resizeRect.leftTouch {
            leftConstraint.constant += deltaX
        }
        if resizeRect.rightTouch {
            rightConstraint.constant -= deltaX
        }
        if resizeRect.bottomTouch {
            bottomConstraint.constant -= deltaY
        }


        UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveEaseIn, animations: {
            self.view.layoutIfNeeded()
        }, completion: { (ended) in

        })
    }
   } 
  }

我也将其作为在Github上工作的项目: https://github.com/ppoh71/resizeRectangleOnTouchDrag


8

以下是@Prerna Chavan解决方案的Swift版本,Prerna的解决方案无法检测用户是否触摸边缘,只能检测角落,但下面的代码可以检测所有情况。

class OverlayView: UIView {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */


    static var kResizeThumbSize:CGFloat = 44.0
    private typealias `Self` = OverlayView

    var imageView = UIImageView()

    var isResizingLeftEdge:Bool = false
    var isResizingRightEdge:Bool = false
    var isResizingTopEdge:Bool = false
    var isResizingBottomEdge:Bool = false

    var isResizingBottomRightCorner:Bool = false
    var isResizingLeftCorner:Bool = false
    var isResizingRightCorner:Bool = false
    var isResizingBottomLeftCorner:Bool = false


        //Define your initialisers here

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let currentPoint = touch.location(in: self)

            isResizingBottomRightCorner = (self.bounds.size.width - currentPoint.x < Self.kResizeThumbSize && self.bounds.size.height - currentPoint.y < Self.kResizeThumbSize);
           isResizingLeftCorner = (currentPoint.x < Self.kResizeThumbSize && currentPoint.y < Self.kResizeThumbSize);
            isResizingRightCorner = (self.bounds.size.width-currentPoint.x < Self.kResizeThumbSize && currentPoint.y < Self.kResizeThumbSize);
            isResizingBottomLeftCorner = (currentPoint.x < Self.kResizeThumbSize && self.bounds.size.height - currentPoint.y < Self.kResizeThumbSize);

            isResizingLeftEdge = (currentPoint.x < Self.kResizeThumbSize)
            isResizingTopEdge = (currentPoint.y < Self.kResizeThumbSize)
            isResizingRightEdge = (self.bounds.size.width - currentPoint.x < Self.kResizeThumbSize)

            isResizingBottomEdge = (self.bounds.size.height - currentPoint.y < Self.kResizeThumbSize)

            // do something with your currentPoint

        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let currentPoint = touch.location(in: self)
            // do something with your currentPoint
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let currentPoint = touch.location(in: self)
            // do something with your currentPoint


            isResizingLeftEdge = false
             isResizingRightEdge = false
             isResizingTopEdge = false
             isResizingBottomEdge = false

             isResizingBottomRightCorner = false
             isResizingLeftCorner = false
             isResizingRightCorner = false
             isResizingBottomLeftCorner = false

        }
    }

0

这是我对@Peter Pohlmann的回答做出的一个小修正,它不允许拖动或移动视图到视图框架之外

唯一需要修改的部分在override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first{
        let currentTouchPoint = touch.location(in: self.view)
        let previousTouchPoint = touch.previousLocation(in: self.view)

        let deltaX = currentTouchPoint.x - previousTouchPoint.x
        let deltaY = currentTouchPoint.y - previousTouchPoint.y

        if resizeRect.middelTouch {
            if topConstraint.constant + deltaY > 0 && leftConstraint.constant + deltaX > 0  && rightConstraint.constant - deltaX > 0 && bottomConstraint.constant - deltaY > 40 {
                topConstraint.constant += deltaY
                leftConstraint.constant += deltaX
                rightConstraint.constant -= deltaX
                bottomConstraint.constant -= deltaY
            }
        }
        if resizeRect.topTouch {
            if topConstraint.constant + deltaY > 0 {
                topConstraint.constant += deltaY
            }
        }
        if resizeRect.leftTouch {
            if leftConstraint.constant + deltaX > 0 {
                leftConstraint.constant += deltaX
            }
        }
        if resizeRect.rightTouch {
            if rightConstraint.constant - deltaX > 0 {
                rightConstraint.constant -= deltaX
            }
        }
        if resizeRect.bottomTouch {
            if bottomConstraint.constant - deltaY > 0 {
                bottomConstraint.constant -= deltaY
            }
        }

        UIView.animate(withDuration: 0.25, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: {
            self.view.layoutIfNeeded()
        }, completion: { (ended) in

        })
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接