Implement Keyboard Key Pop Animation in iOS 8 Keyboard Extension

后端 未结 2 495
耶瑟儿~
耶瑟儿~ 2021-02-01 07:31

I\'d like to ask how to implement the pop animation when holding a keyboard key for iOS 8 keyboards extension. I know how to assign the long press gesture on every key but doesn

2条回答
  •  旧时难觅i
    2021-02-01 07:58

    I would use a CAShapeLayer.

    You can reset the shape layer's shape at any time, and even animate the change in shape to do something more elaborate than the Apple version.

    Here is code for a playground that demonstrates a simple version of a class that accomplishes this:

    import UIKit
    
    class KeyPopView: UIView {
    
      static let widthPadding : CGFloat = 5.0
      static let leftOffset : CGFloat = -5.0
    
      init(frame: CGRect, letters: [String]) {
        super.init(frame: frame)
        addLetters(letters)
      }
    
      required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
      }
    
      override class func layerClass() -> AnyClass {
        return CAShapeLayer.self
      }
    
      override func layoutSubviews() {
        super.layoutSubviews()
        var run : CGFloat = KeyPopView.widthPadding
        for l in labels {
          let s = sizeForLabel(l)
          let mh = maxHeight(labels)
          l.frame = CGRectMake(run, -mh, s.width, s.height)
          run += s.width + KeyPopView.widthPadding
        }
      }
    
      var shapeLayer: CAShapeLayer {
        get {
          return layer as! CAShapeLayer
        }
      }
    
      var path: CGPathRef {
        get {
          return shapeLayer.path
        }
        set(nv) {
          shapeLayer.shadowPath = nv
          shapeLayer.path = nv
        }
      }
    
      var labels : [UILabel] = [] {
        willSet {
          for l in labels {
            l.removeFromSuperview()
          }
        }
        didSet {
          for l in labels {
            addSubview(l)
          }
          path = keyPopPath(labels, cornerRadius: cornerRadius).CGPath
        }
      }
    
      var cornerRadius : CGFloat = 4 {
        didSet {
          path = keyPopPath(labels, cornerRadius: cornerRadius).CGPath
        }
      }
    
      override var backgroundColor: UIColor? {
        set(newValue) {
          shapeLayer.fillColor = newValue?.CGColor
        }
        get {
          return UIColor(CGColor: shapeLayer.fillColor)
        }
      }
    
      func keyPopPath(ls : [UILabel], cornerRadius: CGFloat) -> UIBezierPath {
        let radius = CGSizeMake(cornerRadius, cornerRadius);
        let f = CGRectMake(0, 0, frame.width + KeyPopView.widthPadding * 2, frame.height)
        let mh = maxHeight(ls)
        var b = UIBezierPath(roundedRect: CGRectMake(KeyPopView.leftOffset, -mh, widthForLabels(ls) - KeyPopView.leftOffset + KeyPopView.widthPadding, mh), byRoundingCorners: UIRectCorner.AllCorners, cornerRadii: radius)
        b.appendPath(UIBezierPath(roundedRect: f, byRoundingCorners: UIRectCorner.BottomLeft | UIRectCorner.BottomRight, cornerRadii: radius))
        return b
      }
    
      func addLetters(letters : [String]) {
        labels = letters.map({(s: String) -> UILabel in
          var l = UILabel()
          l.text = s
          return l
        })
      }
    
      func widthForLabels(ls: [UILabel]) -> CGFloat {
        return ls.reduce(0, combine: {(t, l) in t + sizeForLabel(l).width + KeyPopView.widthPadding}) + KeyPopView.widthPadding
      }
    
      func sizeForLabel(l: UILabel) -> CGSize {
        return l.text!.sizeWithAttributes([NSFontAttributeName: l.font])
      }
    
      func maxHeight(ls: [UILabel]) -> CGFloat {
        var m : CGFloat = 0;
        for l in ls {
          let h = sizeForLabel(l).height
          m = m > h ? m : h
        }
        return m
      }
    }
    
    //start with a gray background view
    var ba = UIView(frame: CGRectMake(0, 0, 300, 300))
    ba.backgroundColor = UIColor.grayColor()
    //add a mock "key"
    let key = UILabel()
    key.text = "a"
    key.textAlignment = NSTextAlignment.Center
    key.backgroundColor = UIColor.whiteColor()
    let size = key.text!.sizeWithAttributes([NSFontAttributeName: key.font])
    key.frame = CGRectMake(5, 0, size.width + 10, size.height)
    key.layer.cornerRadius = 5
    key.center = ba.center
    ba.addSubview(key)
    //add the initial keypop
    key.hidden = true // the key's rounded corners aren't showing up correctly in my playground preview -- this shouldn't be necessary
    var k = KeyPopView(frame: CGRectMake(0, 0, size.width, size.height), letters: ["a"])
    k.backgroundColor = UIColor.whiteColor()
    ba.addSubview(k)
    k.center = CGPointMake(key.center.x - 5, key.center.y)
    ba
    //demonstrates resizing of the keypop view to accomdate more letters
    k.addLetters(["a", "b", "c", "d", "e"])
    ba
    

    In its current form, this class has many issues:

    • Letters are slightly off center
    • The frame of the view is used as the frame of the key that the pop begins from, not the actual frame of what is drawn.
    • It only supports left key pops
    • Several optionals are force unwrapped
    • The "stem" of the path used doesn't have rounded inner corners like the System Keyboard
    • Variables are named for brevity, not clarity

    however this should provide a good basis for achieving what you want.

提交回复
热议问题