Skip to content

Commit

Permalink
#26 pop ups now shaped and include characters
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewtavis committed Feb 23, 2022
1 parent 7e5b97a commit 692154d
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 40 deletions.
3 changes: 2 additions & 1 deletion Keyboards/KeyboardsBase/InterfaceVariables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ func setKeyboard() {
// MARK: Alternate Key Variables
var alternatesKeyView: UIView!
var keyCancelled = false
var keyPopCharView: UIView!
var keyPopChar = UILabel()
var keyHoldPopChar = UILabel()
var keyPopLayer = CAShapeLayer()
var keyHoldPopLayer = CAShapeLayer()
var keysWithAlternates = [String]()
Expand Down
203 changes: 169 additions & 34 deletions Keyboards/KeyboardsBase/KeyboardStyling.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,61 +149,139 @@ var leftKeyChars: [String] = [String]()
var rightKeyChars: [String] = [String]()

/// Creates the shape that allows left most buttons to pop up after being pressed.
func leftKeyPopPath( startX: CGFloat, startY: CGFloat, keyWidth: CGFloat, keyHeight: CGFloat) -> UIBezierPath {
///
/// - Parameters
/// - startX: the x-axis starting point.
/// - startY: the y-axis starting point.
/// - keyWidth: the width of the key.
/// - keyHeight: the height of the key.
/// - char: the character of the key.
func leftKeyPopPath(
startX: CGFloat,
startY: CGFloat,
keyWidth: CGFloat,
keyHeight: CGFloat,
char: String) -> UIBezierPath {
// Starting positions need to be updated.
// let horizStart = startX
// let vertStart = startY + keyHeight
let horizStart = startX
let vertStart = startY + keyHeight

// Path is clockwise from bottom left.
let path = UIBezierPath()
path.move(to: CGPoint(x: horizStart, y: vertStart))

// Curve up past bottom left, path up, and curve right past the top left.

// Path right, curve down past the top right, and path down.

// Curve in to the left, go down, and curve down past bottom left.

return path
}

/// Creates the shape that allows right most buttons to pop up after being pressed.
func rightKeyPopPath( startX: CGFloat, startY: CGFloat, keyWidth: CGFloat, keyHeight: CGFloat) -> UIBezierPath {
///
/// - Parameters
/// - startX: the x-axis starting point.
/// - startY: the y-axis starting point.
/// - keyWidth: the width of the key.
/// - keyHeight: the height of the key.
/// - char: the character of the key.
func rightKeyPopPath(
startX: CGFloat,
startY: CGFloat,
keyWidth: CGFloat,
keyHeight: CGFloat,
char: String) -> UIBezierPath {
// Starting positions need to be updated.
// let horizStart = startX
// let vertStart = startY + keyHeight
let horizStart = startX
let vertStart = startY + keyHeight

// Path is clockwise from bottom left.
let path = UIBezierPath()
path.move(to: CGPoint(x: horizStart, y: vertStart))

// Curve up past bottom left, path up, and curve out to the left.

// Path up and curve right past the top left.

// Path right, curve down past the top right, and path down.

// Curve down past bottom left.

return path
}

/// Creates the shape that allows central buttons to pop up after being pressed.
func centerKeyPopPath( startX: CGFloat, startY: CGFloat, keyWidth: CGFloat, keyHeight: CGFloat) -> UIBezierPath {
///
/// - Parameters
/// - startX: the x-axis starting point.
/// - startY: the y-axis starting point.
/// - keyWidth: the width of the key.
/// - keyHeight: the height of the key.
/// - char: the character of the key.
func centerKeyPopPath(
startX: CGFloat,
startY: CGFloat,
keyWidth: CGFloat,
keyHeight: CGFloat,
char: String) -> UIBezierPath {
// Starting positions need to be updated.
let horizStart = startX
let vertStart = startY + keyHeight
var widthMultiplier = 0.0
if DeviceType.isPhone && [".", ",", "?", "!", "'"].contains(char) {
widthMultiplier = 0.2
} else {
widthMultiplier = 0.4
}

// Path is clockwise from bottom left.
let path = UIBezierPath()
path.move(to: CGPoint(x: horizStart, y: vertStart))
path.move(to: CGPoint(x: horizStart + ( keyWidth * 0.075 ), y: vertStart))

// Curve up past bottom left, path up, and curve out to the left.
path.addCurve(to: CGPoint(
x: horizStart,
y: vertStart - ( keyHeight * 0.075 )),
controlPoint1: CGPoint(x: horizStart + ( keyWidth * 0.075 ), y: vertStart - ( keyHeight * 0.005 )),
controlPoint2: CGPoint(x: horizStart, y: vertStart - ( keyHeight * 0.005 )))
path.addLine(to: CGPoint(x: horizStart, y: vertStart - ( keyHeight * 0.85 )))
path.addCurve(to: CGPoint(
x: horizStart - ( keyWidth * widthMultiplier ),
y: vertStart - ( keyHeight * 1.2 )),
controlPoint1: CGPoint(x: horizStart, y: vertStart - ( keyHeight * 0.9 )),
controlPoint2: CGPoint(x: horizStart - ( keyWidth * widthMultiplier ), y: vertStart - ( keyHeight * 1.05 )))

// Go up and curve out to the left.
path.addLine(to: CGPoint(x: horizStart, y: vertStart - ( keyHeight * 0.95 )))
path.addLine(to: CGPoint(x: horizStart - ( keyWidth * 0.4 ), y: vertStart - ( keyHeight * 0.95 )))
// path.addCurve(to: CGPoint(
// x: horizStart - ( keyWidth * 0.75 / 2 ),
// y: vertStart + ( keyHeight * 1.1 )),
// controlPoint1: CGPoint(x: horizStart - ( keyWidth * 0.75 ), y: vertStart + ( keyHeight * 0.75 )),
// controlPoint2: CGPoint(x: horizStart, y: vertStart + ( keyHeight * 0.75 )))

// Path to top left, top right and back down.
path.addLine(to: CGPoint(x: horizStart - ( keyWidth * 0.4 ), y: vertStart - ( keyHeight * 2.125 )))
path.addLine(to: CGPoint(x: horizStart + ( keyWidth * 1.4 ), y: vertStart - ( keyHeight * 2.125 )))
path.addLine(to: CGPoint(x: horizStart + ( keyWidth * 1.4 ), y: vertStart - ( keyHeight * 0.95 )))

// Curve in to the left and go down to bottom right.
path.addLine(to: CGPoint(x: horizStart + keyWidth, y: vertStart - ( keyHeight * 0.95 )))
// path.addCurve(to: CGPoint(
// x: horizStart + keyWidth,
// y: vertStart + ( keyHeight * 0.7 )),
// controlPoint1: CGPoint(x: horizStart + keyWidth - ( keyWidth * 0.25 ), y: vertStart + ( keyHeight * 0.75 )),
// controlPoint2: CGPoint(x: horizStart + keyWidth + ( keyWidth * 0.25 ), y: vertStart + ( keyHeight * 0.75 )))
path.addLine(to: CGPoint(x: horizStart + keyWidth, y: vertStart))
// Path up and curve right past the top left.
path.addLine(to: CGPoint(x: horizStart - ( keyWidth * widthMultiplier ), y: vertStart - ( keyHeight * 1.8 )))
path.addCurve(to: CGPoint(
x: horizStart + ( keyWidth * 0.075 ),
y: vertStart - ( keyHeight * 2.125 )),
controlPoint1: CGPoint(x: horizStart - ( keyWidth * widthMultiplier ), y: vertStart - ( keyHeight * 2 )),
controlPoint2: CGPoint(x: horizStart - ( keyWidth * 0.25 ), y: vertStart - ( keyHeight * 2.125 )))

// Path right, curve down past the top right, and path down.
path.addLine(to: CGPoint(x: horizStart + ( keyWidth * 0.925 ), y: vertStart - ( keyHeight * 2.125 )))
path.addCurve(to: CGPoint(
x: horizStart + ( keyWidth * ( 1 + widthMultiplier ) ),
y: vertStart - ( keyHeight * 1.8 )),
controlPoint1: CGPoint(x: horizStart + ( keyWidth * 1.25 ), y: vertStart - ( keyHeight * 2.125 )),
controlPoint2: CGPoint(x: horizStart + ( keyWidth * ( 1 + widthMultiplier ) ), y: vertStart - ( keyHeight * 2 )))
path.addLine(to: CGPoint(x: horizStart + ( keyWidth * ( 1 + widthMultiplier ) ), y: vertStart - ( keyHeight * 1.2 )))

// Curve in to the left, go down, and curve down past bottom left.
path.addCurve(to: CGPoint(
x: horizStart + keyWidth,
y: vertStart - ( keyHeight * 0.85 )),
controlPoint1: CGPoint(x: horizStart + ( keyWidth * ( 1 + widthMultiplier ) ), y: vertStart - ( keyHeight * 1.05 )),
controlPoint2: CGPoint(x: horizStart + keyWidth, y: vertStart - ( keyHeight * 0.9 )))
path.addLine(to: CGPoint(x: horizStart + keyWidth, y: vertStart - ( keyHeight * 0.075 )))
path.addCurve(to: CGPoint(
x: horizStart + ( keyWidth * 0.925 ),
y: vertStart),
controlPoint1: CGPoint(x: horizStart + keyWidth, y: vertStart - ( keyHeight * 0.005 )),
controlPoint2: CGPoint(x: horizStart + ( keyWidth * 0.925 ), y: vertStart - ( keyHeight * 0.005 )))

path.close()
return path
Expand All @@ -215,7 +293,55 @@ func centerKeyPopPath( startX: CGFloat, startY: CGFloat, keyWidth: CGFloat, keyH
/// - key: the key pressed.
/// - layer: the layer to be set.
/// - char: the character of the key.
func genKeyPop(key: UIButton, layer: CAShapeLayer, char: String) {
/// - displayChar: the character to display on the pop up.
func genKeyPop(key: UIButton, layer: CAShapeLayer, char: String, displayChar: String) {
if DeviceType.isPhone {
if shiftButtonState == .shift || shiftButtonState == .caps {
if isLandscapeView == true {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.25)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.25)
} else {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 1)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 1)
}
} else {
if isLandscapeView == true {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 2)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 2)
} else {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 0.9)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 0.9)
}
}
} else if DeviceType.isPad {
if shiftButtonState == .shift || shiftButtonState == .caps {
if isLandscapeView == true {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 3)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 3)
} else {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.5)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.5)
}
} else {
if isLandscapeView == true {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.75)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.75)
} else {
keyPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.25)
keyHoldPopChar.font = .systemFont(ofSize: letterKeyWidth / 2.25)
}
}
}

let popLbls: [UILabel] = [keyPopChar, keyHoldPopChar]
for lbl in popLbls {
lbl.text = displayChar
lbl.backgroundColor = .clear
lbl.textAlignment = .center
lbl.textColor = keyCharColor
lbl.sizeToFit()
}

// Get the frame in respect to the superview.
let frame: CGRect = (key.superview?.convert(key.frame, to: nil))!

Expand All @@ -224,19 +350,28 @@ func genKeyPop(key: UIButton, layer: CAShapeLayer, char: String) {
startX: frame.origin.x,
startY: frame.origin.y,
keyWidth: key.frame.width,
keyHeight: key.frame.height).cgPath
keyHeight: key.frame.height,
char: char).cgPath
keyPopChar.center = CGPoint(
x: frame.origin.x + key.frame.width / 2,
y: frame.origin.y - key.frame.height / 1.75)
keyHoldPopChar.center = CGPoint(
x: frame.origin.x + key.frame.width / 2,
y: frame.origin.y - key.frame.height / 1.75)
} else if leftKeyChars.contains(char) {
layer.path = leftKeyPopPath(
startX: frame.origin.x,
startY: frame.origin.y,
keyWidth: key.frame.width,
keyHeight: key.frame.height).cgPath
keyHeight: key.frame.height,
char: char).cgPath
} else if rightKeyChars.contains(char) {
layer.path = rightKeyPopPath(
startX: frame.origin.x,
startY: frame.origin.y,
keyWidth: key.frame.width,
keyHeight: key.frame.height).cgPath
keyHeight: key.frame.height,
char: char).cgPath
}

layer.strokeColor = keyShadowColor
Expand Down
21 changes: 16 additions & 5 deletions Keyboards/KeyboardsBase/KeyboardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ class KeyboardViewController: UIInputViewController {
target: self,
action: #selector(genHoldPopUpView(sender:))
)
keyHoldPop.minimumPressDuration = 0.1
keyHoldPop.minimumPressDuration = 0.125

if allNonSpecialKeys.contains(key) {
btn.addTarget(self, action: #selector(genPopUpView), for: .touchDown)
Expand Down Expand Up @@ -1482,11 +1482,14 @@ class KeyboardViewController: UIInputViewController {
/// - key: the key pressed.
@objc func genPopUpView(key: UIButton) {
let charPressed: String = key.layer.value(forKey: "original") as? String ?? ""
genKeyPop(key: key, layer: keyPopLayer, char: charPressed)
let displayChar: String = key.layer.value(forKey: "keyToDisplay") as? String ?? ""
genKeyPop(key: key, layer: keyPopLayer, char: charPressed, displayChar: displayChar)

self.view.layer.addSublayer(keyPopLayer)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.view.addSubview(keyPopChar)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.125) {
keyPopLayer.removeFromSuperlayer()
keyPopChar.removeFromSuperview()
}
}

Expand All @@ -1495,18 +1498,22 @@ class KeyboardViewController: UIInputViewController {
/// - Parameters
/// - sender: the long press of the given key.
@objc func genHoldPopUpView(sender: UILongPressGestureRecognizer) {
let startTime = Date()

// Derive which button was pressed and get its alternates.
guard let key: UIButton = sender.view as? UIButton else { return }
let charPressed: String = key.layer.value(forKey: "original") as? String ?? ""

genKeyPop(key: key, layer: keyHoldPopLayer, char: charPressed)
let displayChar: String = key.layer.value(forKey: "keyToDisplay") as? String ?? ""
genKeyPop(key: key, layer: keyHoldPopLayer, char: charPressed, displayChar: displayChar)

if sender.state == .began {
self.view.layer.addSublayer(keyHoldPopLayer)
self.view.addSubview(keyHoldPopChar)
keyCancelled = false
} else if sender.state == .changed {
// Remove the key hold pop up and alternates view if user cancels.
keyHoldPopLayer.removeFromSuperlayer()
keyHoldPopChar.removeFromSuperview()
keyCancelled = true
if self.view.viewWithTag(1001) != nil {
let viewWithTag = self.view.viewWithTag(1001)
Expand All @@ -1516,8 +1523,11 @@ class KeyboardViewController: UIInputViewController {
} else if sender.state == .ended && keyCancelled == false {
// Remove the key hold pop up and execute key only if the alternates view isn't present.
keyHoldPopLayer.removeFromSuperlayer()
keyHoldPopChar.removeFromSuperview()
if !keysWithAlternates.contains(charPressed) {
executeKeyActions(key)
} else if Date() < Calendar.current.date(byAdding: .second, value: 1, to: startTime) ?? Date() {
executeKeyActions(key)
}
keyUntouched(key)
}
Expand All @@ -1526,6 +1536,7 @@ class KeyboardViewController: UIInputViewController {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.setAlternatesView(sender: sender)
keyHoldPopLayer.removeFromSuperlayer()
keyHoldPopChar.removeFromSuperview()
}
}
}
Expand Down

0 comments on commit 692154d

Please sign in to comment.