How can I add sizing handles to a UIView?
Asked Answered
D

1

6

I'm trying to dynamically create views (UIImageView and UITextView) at runtime by user request and then allow the user to move and resize them. I've got everything working great, except for the resizing. I tried using the pinch gesture recognizer, but find it too clumsy for what I want. Therefore, I would like to use sizing handles. I believe I could put a pan gesture recognizer on each handle, and adjust the view frame as one of them is moved.

enter image description here

The problem is, I'm not quite sure how to create the sizing handles. I would indicate all the things I've tried, but truthfully, I'm not quite sure where to start. I do have a few ideas...

1) Possibly use coregraphics to draw boxes or circles on the corners and sides? Would I create a new layer and draw them on that? Not sure.

2) Stick a little image of a box or circle on each corner?

3) XIB file with the handles already placed on it?

Any suggestions appreciated. I just need to be pointed in the right direction.

Edit: Something like what Apple uses in Pages would be perfect!

enter image description here

enter image description here

Decidua answered 24/4, 2016 at 5:25 Comment(0)
T
5

First, I suggest create a custom View subclass to UIView, you will handle all of the behavior here. Let's call it ResizableView.

In the custom view, You need to draw layer or view for these dot at corner and add PangestureRecognized to them.Then you can track the location of these dot using recognizer.locationInView() when user drag them, which you will use to calculate the scale of View.Here is the code you can refer to:

func rotateViewPanGesture(recognizer: UIPanGestureRecognizer) {
    touchLocation = recognizer.locationInView(self.superview)

    let center = CalculateFunctions.CGRectGetCenter(self.frame)

    switch recognizer.state {
    case .Began:
        initialBounds = self.bounds
        initialDistance = CalculateFunctions.CGpointGetDistance(center, point2: touchLocation!)


    case .Changed:        

        //Finding scale between current touchPoint and previous touchPoint
        let scale = sqrtf(Float(CalculateFunctions.CGpointGetDistance(center, point2: touchLocation!)) / Float(initialDistance!))
        let scaleRect = CalculateFunctions.CGRectScale(initialBounds!, wScale: CGFloat(scale), hScale: CGFloat(scale))

        self.bounds = scaleRect

        self.refresh()

    case:.Ended:
          self.refresh() 

    default:break

Step by step

touchLocation location of the Pangesture

center is the center of ResizableView

initialBounds is the initial bounds of the ResizableView when PanGesture begin.

initailDistance is the distance between the center of the ResizableView of touchPoint of the dot the user is dragging.

Then you can calculate the scale given initialDistance, center, touch location

Now you have scaled the view as You want. You also need to refresh the position of these dot at corner accordingly, that's what refresh() for, you need to implement it yourself.

CalculateFunctions

I tend to define some helpFunctions to help me calculate.

CalculateFunctions.CGPointGetDistance is used to calculate the distance between center of the view and touch location.

CalculateFunctions.CGRectScale return the scaled CGRect given the the scale you just calculated.

CalculateFunctions.CGRectGetCenter return the center point of a CGRect

That's just a rough idea. Actually there are many Libraries you can refer to. Some suggestions:

  • SPUserResizableView This is a ResizableView exactly what you want, but it was written in ObjC and hasn't been updated for a long time. But you can refer to it.
  • JLStickerTextView This may not fit your requirement very well as it is for text(edit, resize, rotate with one finger) However, this one is written in Swift, a good example for you.

If you have any questions, feel free to post it.Good Luck:-)

Teddi answered 24/4, 2016 at 8:38 Comment(4)
Thanks so much. SPUserResizableView is actually perfect. I'm going to see if I can migrate it over to Swift. I will post my results.Decidua
Just been working on this myself. Rather than converting the code of SPUserResizableView you can use Pods - pod ‘SPUserResizableView+Pion’ then add to your viewController.swift import SPUserResizableView_PionHoldback
You can also take a look at this lib github.com/zedoul/ZDStickerViewConnelley
Does anyone know a modern solution to this? With latest Swift and SwiftUI?Criss

© 2022 - 2024 — McMap. All rights reserved.