How to resize UIView by dragging from its edges?

后端 未结 6 1132
-上瘾入骨i
-上瘾入骨i 2020-11-28 21:29

In my iPad app, I want the users to be able to resize a UIView by dragging the view from its edges. I\'ll be using iOS 5 SDK, so what\'s the cleanest approach t

相关标签:
6条回答
  • 2020-11-28 21:37

    I updated above code using 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 saves state of touch position of user.

    0 讨论(0)
  • 2020-11-28 21:37

    Swift Version of @Prerna Chavan solution , Prerna solution does not detect if user touch on edges, it is detecting only corners, however below code detects all

    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 讨论(0)
  • 2020-11-28 21:42

    I'm guessing your UI involves some kind of handles on the sides of the view, and attaching a simple UIPanGestureRecognizer to those handle controls makes the whole problem pretty easy.

    In the action method from the gesture recognizer, just get the -translationInView: relative to the view you're resizing, save off the original frame when the gesture recognizer's state is UIGestureRecognizerStateBegan, and adjust the view's frame continually while the state is UIGestureRecognizerStateChanged.

    0 讨论(0)
  • 2020-11-28 21:46

    Just a small fixed added by me to @Peter Pohlmann answer, that doesn't allow dragging or moving the view outside the view's frame

    The only bit is in 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
    
            })
        }
    }
    
    0 讨论(0)
  • 2020-11-28 21:49

    You can do this by checking the touch-start point. If it hits one of your four corners you can resize based on the distance between that touch-start point and the current-touch point. (If the touch-start point didn't hit a corner, we just move the view instead of resizing.)

    Define the size of your draggable corners.

    CGFloat kResizeThumbSize = 45.0f;
    

    Add these instance variables to your class to keep track of touch state and which way we're resizing.

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

    Handle the touch start / change events.

    - (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);
        }
    } 
    
    0 讨论(0)
  • 2020-11-28 21:53

    This is a Swift 4.2 Solution which works with AutoLayout & Constraint-Animation.

    Since it is recommended to animate the constraints rather than the actual size of a rectangle when working with AutoLayout, this solutions does exactly that.

    Additional features:

    • Resize only one side when not on the edge.
    • Move the whole rectangle on touch in the middle of the it.

    Checkout a video of it here: https://imgur.com/a/CrkARLi

    Import is to connect the constraints as outlets, to animate them.

    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
    
            })
        }
       } 
      }
    

    I put this as working project on Github too: https://github.com/ppoh71/resizeRectangleOnTouchDrag

    0 讨论(0)
提交回复
热议问题