简体   繁体   中英

How to Increase/Decrease font size with Pan Gesture?

I am applying pan gesture on UILabel . I use one finger scale to increase and decrease the size of the UILabel .

I tried using scale to add and subtract value from font size, but I am not getting the exact result.

@objc
func handleRotateGesture(_ recognizer: UIPanGestureRecognizer) {
    let touchLocation = recognizer.location(in: self.superview)
    let center = self.center

    switch recognizer.state {
    case .began:

        self.deltaAngle = CGFloat(atan2f(Float(touchLocation.y - center.y), Float(touchLocation.x - center.x))) - CGAffineTransformGetAngle(self.transform)
        self.initialBounds = self.bounds
        self.initialDistance = CGPointGetDistance(point1: center, point2: touchLocation)

    case .changed:
        let angle = atan2f(Float(touchLocation.y - center.y), Float(touchLocation.x - center.x))
        let angleDiff = Float(self.deltaAngle) - angle
        self.transform = CGAffineTransform(rotationAngle: CGFloat(-angleDiff))
        if let label = self.contentView as? UILabel {


            var scale = CGPointGetDistance(point1: center, point2: touchLocation) / self.initialDistance
            let minimumScale = CGFloat(self.minimumSize) / min(self.initialBounds.size.width, self.initialBounds.size.height)
            scale = max(scale, minimumScale)
            let scaledBounds = CGRectScale(self.initialBounds, wScale: scale, hScale: scale)


            var pinchScale = scale
            pinchScale = round(pinchScale * 1000) / 1000.0


            var fontSize = label.font.pointSize
            if(scale > minimumScale){
                if (self.bounds.height > scaledBounds.height) {
                     //  fontSize = fontSize - pinchScale
                       label.font = UIFont(name: label.font.fontName, size: fontSize - pinchScale)
                   }
                   else{
                      // fontSize = fontSize + pinchScale
                       label.font = UIFont(name: label.font.fontName, size: fontSize + pinchScale)
                   }
            } else {
                label.font = UIFont( name: label.font.fontName, size: fontSize)
            }

             print("PinchScale -- \(pinchScale), FontSize = \(fontSize)")



            self.bounds = scaledBounds

        } else {
            var scale = CGPointGetDistance(point1: center, point2: touchLocation) / self.initialDistance
            let minimumScale = CGFloat(self.minimumSize) / min(self.initialBounds.size.width, self.initialBounds.size.height)
            scale = max(scale, minimumScale)
            let scaledBounds = CGRectScale(self.initialBounds, wScale: scale, hScale: scale)
            self.bounds = scaledBounds
        }

        self.setNeedsDisplay()
    default:
        break
    }
}

However, we can achieve this using UIPinchGestureRecognizer . But how can we do the same effect with UIPanGestureRecognizer ? Any help would be appreciated. Thanks.

Right now you're adding and removing points to the label's current font size. I'd suggest a simpler pattern, more consistent with what you're doing elsewhere, namely just capturing the the initial point size at the start of the gesture and then, as the user's finger moves, apply a scale to that saved value. So, I'd suggest:

  • Define property to save the initial font size:

     var initialPointSize: CGFloat = 0
  • In the .began of the gesture, capture the current size

    initialPointSize = (contentView as? UILabel)?.font?.pointSize ?? 0
  • In the .changed , adjust the font size:

     let pinchScale = (scale * 1000).rounded() / 1000 label.font = label.font.withSize(initialPointSize * pinchScale)

As an aside, I'm not sure that it's necessary to round the scale to three decimal places, but you had that in your original code snippet, so I preserved that.


Personally, I'd follow the same basic pattern with the transform :

  • Define a properties to capture the starting angle and the view's current transform :

     var initialAngle: CGFloat = 0 var initialTransform: CGAffineTransform = .identity
  • In the .began , capture the current the starting angle and existing transform:

     initialAngle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) initialTransform = transform
  • In the .changed , update the transform :

     let angle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) transform = initialTransform.rotated(by: angle - initialAngle)

This saves you from reverse engineering the angle associated with the current transform with CGAffineTransformGetAngle and instead just apply rotated(by:) to the saved transform.

So this, like the point size and the bounds, represents a consistent pattern: Capture the starting value in .began and just apply whatever change you need in .changed .


A few unrelated observations:

  1. All those self. references are not needed. It just adds noise that makes it harder to read the code.

  2. All of those casts between CGFloat and Float are not needed. If you use the atof2 function instead of atof2f , they all just work with CGFloat without any casting. Eg, instead of

    let angle = atan2f(Float(touchLocation.y - center.y), Float(touchLocation.x - center.x)) let angleDiff = Float(self.deltaAngle) - angle self.transform = CGAffineTransform(rotationAngle: CGFloat(-angleDiff))

    You can just do:

     let angle = atan2(touchLocation.y - center.y, touchLocation.x - center.x) transform = CGAffineTransform(rotationAngle: angle - deltaAngle)

    You actually don't need any of those casts that are currently sprinkled throughout the code snippet.

  3. All of those bounds.size.width and bounds.size.height can just be bounds.width and bounds.height respectively, again removing noise from the code.

  4. When adjusting the font size, rather than:

     label.font = UIFont(name: label.font.fontName, size: fontSize)

    You should just use:

     label.font = label.font.withSize(fontSize)

    That way you preserve all of the essential characteristics of the font (the weight, etc.), and just adjust the size.

  5. In your if let label = contentView as? UILabel if let label = contentView as? UILabel test, the same five lines of code in your else clause appear in the if clause, too. You should just move those common lines before the if - else statement, and then you can lose the else clause entirely, simplifying your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM