在iOS 8键盘扩展中实现键盘按键弹出动画

17

我想请问如何在iOS 8键盘扩展中按住键盘键时实现弹出动画。我知道如何为每个键分配长按手势,但不知道如何使键以特定的方式呈现其他字符。

编辑:我看到有人提出了类似的问题here,但不同之处在于他能够创建弹出动画。

编辑2:我看到另一个类似的问题here,但它们与标准键盘上的默认外观相比有所不同。

tapped expanded keyboard key

编辑3:当点击键盘键时,我已成功实现所需的行为。 我只需要知道如何正确绘制扩展键视图。 以下是参考图片。 第一张图片是目前为止我们已经实现的情况。 我想知道如何绘制那个字母F键,然后将其转换为UIView

current progress

编辑4:我已经创建了键盘弹出视图,但不是我想要的形状或层次结构,它与标准键盘键弹出相似。这是参考图片:

current key pop view

编辑5:我尝试了PaintCode的演示版本,并生成了以下代码。这是我自定义视图上drawRect方法中的内容。我的键宽通常为26.0,高度为39.0。 我也在使用Objective-C。

    UIBezierPath* bezierPath = UIBezierPath.bezierPath;
    [bezierPath moveToPoint: CGPointMake(26, 5.12)];
    [bezierPath addLineToPoint: CGPointMake(26, 18.03)];
    [bezierPath addCurveToPoint: CGPointMake(23.05, 22.41) controlPoint1: CGPointMake(26, 19.88) controlPoint2: CGPointMake(24.82, 21.51)];
    [bezierPath addCurveToPoint: CGPointMake(19.62, 25.27) controlPoint1: CGPointMake(22.05, 23.24) controlPoint2: CGPointMake(20.79, 24.3)];
    [bezierPath addCurveToPoint: CGPointMake(19.62, 39.95) controlPoint1: CGPointMake(19.62, 30.82) controlPoint2: CGPointMake(19.62, 39.95)];
    [bezierPath addCurveToPoint: CGPointMake(17.17, 42) controlPoint1: CGPointMake(19.62, 41.08) controlPoint2: CGPointMake(18.52, 42)];
    [bezierPath addLineToPoint: CGPointMake(8.83, 42)];
    [bezierPath addCurveToPoint: CGPointMake(6.38, 39.95) controlPoint1: CGPointMake(7.48, 42) controlPoint2: CGPointMake(6.38, 41.08)];
    [bezierPath addCurveToPoint: CGPointMake(6.38, 25.33) controlPoint1: CGPointMake(6.38, 39.95) controlPoint2: CGPointMake(6.38, 30.89)];
    [bezierPath addCurveToPoint: CGPointMake(5.67, 24.74) controlPoint1: CGPointMake(6.15, 25.14) controlPoint2: CGPointMake(5.91, 24.94)];
    [bezierPath addCurveToPoint: CGPointMake(5.37, 24.49) controlPoint1: CGPointMake(5.57, 24.66) controlPoint2: CGPointMake(5.47, 24.57)];
    [bezierPath addLineToPoint: CGPointMake(5.32, 24.45)];
    [bezierPath addCurveToPoint: CGPointMake(2.75, 22.3) controlPoint1: CGPointMake(4.41, 23.69) controlPoint2: CGPointMake(3.5, 22.93)];
    [bezierPath addCurveToPoint: CGPointMake(1.02, 20.85) controlPoint1: CGPointMake(2.06, 21.92) controlPoint2: CGPointMake(1.47, 21.43)];
    [bezierPath addCurveToPoint: CGPointMake(0.98, 20.82) controlPoint1: CGPointMake(0.99, 20.83) controlPoint2: CGPointMake(0.98, 20.82)];
    [bezierPath addCurveToPoint: CGPointMake(0, 18.03) controlPoint1: CGPointMake(0.36, 20.02) controlPoint2: CGPointMake(-0, 19.06)];
    [bezierPath addLineToPoint: CGPointMake(0, 5.12)];
    [bezierPath addCurveToPoint: CGPointMake(2.48, 1.01) controlPoint1: CGPointMake(0, 3.44) controlPoint2: CGPointMake(0.97, 1.94)];
    [bezierPath addCurveToPoint: CGPointMake(6.05, 0) controlPoint1: CGPointMake(3.48, 0.39) controlPoint2: CGPointMake(4.71, 0.02)];
    [bezierPath addLineToPoint: CGPointMake(6.13, 0)];
    [bezierPath addLineToPoint: CGPointMake(19.87, 0)];
    [bezierPath addCurveToPoint: CGPointMake(26, 5.12) controlPoint1: CGPointMake(23.25, 0) controlPoint2: CGPointMake(26, 2.29)];
    [bezierPath closePath];
    [[UIColor redColor] setFill];
    [bezierPath fill];

问题是,它看起来像这样:

在此输入图片描述

只要我能把它放大到像默认键盘那样大,那么它就可以运行。


你可以使用 CGAffineTransform 来缩放贝塞尔路径的 CGPath 属性的副本,直到它足够大。 - Ben Pious
1
请查看此链接。我已经添加了我的弹出按钮代码:https://dev59.com/sITba4cB1Zd3GeqP1yXZ#33355236 - Muzammil
感谢Muzammil先生:) - jaytrixz
你能分享一下你的想法吗?我开发了一个自定义键盘,而不是应用程序扩展。我必须为一些按键显示弹出窗口。我的键盘包含数学按键。例如,当用户点击平方根按钮时,必须在弹出窗口中显示平方根、立方根和第n根。 - Piraba
2个回答

10
我会使用一个 `CAShapeLayer`。
你可以随时重置形状图层的形状,甚至可以动画地改变形状,以执行比苹果版本更复杂的操作。
这里是一个示例代码,演示了一个简单版本的类来实现这一点:
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

在当前形式下,这个类存在许多问题:
  • 字母稍微偏离中心
  • 视图的框架被用作弹出开始的键的框架,而不是实际绘制内容的框架。
  • 它仅支持向左弹出键
  • 有几个可选项被强制解包
  • 路径的“干”部分没有像系统键盘那样圆角内侧
  • 变量名称简洁,但不够清晰易懂
然而,这应该为实现您想要的提供一个良好的基础。

谢谢,但是键弹出形状也几乎和我想到的那个一样。请查看问题描述的更新。 - jaytrixz
为了更接近您想要的确切形状,您可以编辑由keypopPath创建的路径,以附加曲线到茎的内部拐角(您可能需要调整形状图层/路径的绕组规则才能使其正常工作),或者从头开始创建一系列点和曲线的贝塞尔路径。实际上,Rambo发布的链接中有使用此方法的代码 https://github.com/illyabusigin/CYRKeyboardButton/blob/master/CYRKeyboardButton/CYRKeyboardButtonView.m 中的 inputViewPath 方法,这应该很容易适应您特定的用例。 - Ben Pious

0

可能最简单的方法是在Photoshop或其他可以将动画帧输出为单独图像的软件中制作动画,然后使用UIImageViewdocs)进行动画处理。

因此,只需按上述方式使弹出框的背景动画化,然后可以选择让字母随着动画淡入,或者仔细地将UILabel与背景动画一起进行动画处理。


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