How to create a custom iOS keyboard - ios

First off, I will have you know that I have looked at many many different posts on StackOverflow and on Google & Github. I have scoured everywhere for ANYTHING that will help me. But, nothing seems to work. Either it's out of date (by 10+ years) or written entirely in Objective-C. I know that there are other posts out there about this topic, but I need a solution in Swift 5.0 +, not an ancient one from 11 years ago in Obj-C that is totally deprecated in everything today.
Now, my question. I need to develop a keyboard for my iOS app. It needs to be a number/operator type keyboard, effectively a basic maths keyboard.
I have no idea how or where to start, I am relatively new to Swift/iOS development in general.
I have tried to use KeyboardKit, (See Github Page) but have had no luck; the documentation is extremely minimal, not anywhere close to enough for a beginner to take and use effectively.
I have tried many different GitHub repos, but none have fit what I need.
In a summary, my keyboard needs to be set as the default keyboard for the app, (but I'll ask this a a different question later); so it needs that option. It needs to be fully (easily =) customizable. And it needs to act just like the default Apple keyboard.
I am looking for something relevant, and extensible
Cheers!

I have solved my problem. I discovered this site
on StackOverflow that completely solved my issues. I was able to add customized keys to the keyboard, which was my primary issue.
I created a class called DigitButton.swift.
import UIKit
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: (UIKeyInput & UITextInput)?
var useDecimalSeparator: Bool
lazy var parenthesis1: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "("
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapParenthesis1(_:)), for: .touchUpInside)
return button
}()
lazy var squareroot: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "√"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapSquareRoot(_:)), for: .touchUpInside)
return button
}()
lazy var parenthesis2: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = ")"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapParenthesis2), for: .touchUpInside)
return button
}()
lazy var exponentButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^0"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton(_:)), for: .touchUpInside)
return button
}()
lazy var exponentButton2: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^2"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton2(_:)), for: .touchUpInside)
return button
}()
lazy var exponentButton3: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^3"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton3(_:)), for: .touchUpInside)
return button
}()
lazy var exponentButton4: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "^"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapExponentButton4(_:)), for: .touchUpInside)
return button
}()
lazy var addButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "+"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapAddButton(_:)), for: .touchUpInside)
return button
}()
lazy var subtractButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "-"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapSubtractButton(_:)), for: .touchUpInside)
return button
}()
lazy var divideButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "/"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapDivideButton(_:)), for: .touchUpInside)
return button
}()
lazy var multiplyButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = "*"
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapMultiplyButton(_:)), for: .touchUpInside)
return button
}()
lazy var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = $0
button.setTitle("\($0)", for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .title1)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.sizeToFit()
button.titleLabel?.numberOfLines = 1
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.lineBreakMode = .byTruncatingTail
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.addTarget(self, action: #selector(didTapDigitButton(_:)), for: .touchUpInside)
button.inputView.self?.sizeToFit()
return button
}
var deleteButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("⌫", for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = "Delete"
button.addTarget(self, action: #selector(didTapDeleteButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput & UITextInput, useDecimalSeparator: Bool = false) {
self.target = target
self.useDecimalSeparator = useDecimalSeparator
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Actions
extension NumericKeyboard {
#objc func didTapSquareRoot(_ sender: DigitButton) {
insertText("√")
}
#objc func didTapParenthesis1(_ sender: DigitButton) {
insertText("(")
}
#objc func didTapParenthesis2(_ sender: DigitButton) {
insertText(")")
}
#objc func didTapDigitButton(_ sender: DigitButton) {
insertText("\(sender.digit)")
}
#objc func didTapDecimalButton(_ sender: DigitButton) {
insertText(Locale.current.decimalSeparator ?? ".")
}
#objc func didTapExponentButton(_ sender: DigitButton){
insertText("^0")
}
#objc func didTapExponentButton2(_ sender: DigitButton){
insertText("^2")
}
#objc func didTapExponentButton3(_ sender: DigitButton){
insertText("^3")
}
#objc func didTapExponentButton4(_ sender: DigitButton){
insertText("^")
}
#objc func didTapAddButton(_ sender: DigitButton){
insertText("+")
}
#objc func didTapSubtractButton(_ sender: DigitButton){
insertText("-")
}
#objc func didTapDivideButton(_ sender: DigitButton){
insertText("/")
}
#objc func didTapMultiplyButton(_ sender: DigitButton){
insertText("*")
}
#objc func didTapDeleteButton(_ sender: DigitButton) {
target?.deleteBackward()
}
}
// MARK: - Private initial configuration methods
private extension NumericKeyboard {
func configure() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
addButtons()
}
func addButtons() {
let stackView = createStackView(axis: .vertical)
stackView.frame = bounds
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(stackView)
for row in 0 ..< 3 {
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
for column in 0 ..< 3 {
subStackView.addArrangedSubview(numericButtons[row * 3 + column + 1])
}
}
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
subStackView.addArrangedSubview(numericButtons[0])
subStackView.addArrangedSubview(parenthesis1)
subStackView.addArrangedSubview(parenthesis2)
subStackView.addArrangedSubview(squareroot)
subStackView.addArrangedSubview(addButton)
subStackView.addArrangedSubview(subtractButton)
subStackView.addArrangedSubview(multiplyButton)
subStackView.addArrangedSubview(divideButton)
subStackView.addArrangedSubview(exponentButton)
subStackView.addArrangedSubview(exponentButton2)
subStackView.addArrangedSubview(exponentButton4)
subStackView.addArrangedSubview(deleteButton)
}
func createStackView(axis: NSLayoutConstraint.Axis) -> UIStackView {
let stackView = UIStackView()
stackView.axis = axis
stackView.alignment = .fill
stackView.distribution = .fillProportionally
return stackView
}
func insertText(_ string: String) {
guard let range = target?.selectedRange else { return }
if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false {
return
}
if let textView = target as? UITextView, textView.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: string) == false {
return
}
target?.insertText(string)
}
}
// MARK: - UITextInput extension
extension UITextInput {
var selectedRange: NSRange? {
guard let textRange = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: textRange.start)
let length = offset(from: textRange.start, to: textRange.end)
return NSRange(location: location, length: length)
}
}
Then I set my InputView input method with textField.inputView = NumericKeyboard(target: textField)
This has worked perfectly.

Related

UIButton title and image positions are swapped unexpectedly on clicked

I'd like to make a button that the image is on the right instead of the left side.
This is the expected layout:
I'm able to satisfy the layouts with the following code:
import UIKit
class ViewController: UIViewController {
private lazy var leftButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
button.setImage(UIImage(systemName: "trash"), for: .normal)
button.setTitle("Trash", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
if let imageView = button.imageView {
imageView.contentMode = .scaleAspectFit
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalToConstant: 8),
imageView.heightAnchor.constraint(equalToConstant: 8)
])
}
button.contentHorizontalAlignment = .left
button.contentVerticalAlignment = .center
button.semanticContentAttribute = .forceRightToLeft
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: leftButton)
}
#objc private func didTap() {
}
}
Everything looked exactly what I wanted at the beginning until I clicked the button. The positions of the title and the image were swapped. Please take a look at the gif:
Does anyone have any clue on why this happened? And how can I improve my code?
I guess it may have something to do with the navigation item.
Try it like this:
let button = UIButton(type: .system)
button.setTitle("Trash", for: .normal)
button.setImage(UIImage(systemName: "trash"), for: .normal)
button.setPreferredSymbolConfiguration(
.init(scale: .small), forImageIn: .normal
)
button.widthAnchor.constraint(equalToConstant: 60).isActive = true
button.imageEdgeInsets.right = -75
button.titleEdgeInsets.left = -35
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
However, if you can confine yourself to iOS 15 and later, this is a lot more simple and coherent, because the notion of a trailing image is built right in:
var config = UIButton.Configuration.plain()
config.title = "Trash"
config.image = UIImage(systemName: "trash")?
.applyingSymbolConfiguration(.init(scale: .small))
config.imagePlacement = .trailing
let button = UIButton(configuration: config)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)

Using Multiple Cases in a Switch Statement

I'm trying to use a switch statement on a three button action where the user can select between morning, afternoon and evening times. I'm wanting to change the background and text colors based on what button is selected.
Right now my buttons are not changing based on tap, what do I need to do in order to get this working? Also, how can I use like tag 10 and tag 20 in one of the cases? Thank you.
// My Buttons
private lazy var morningButton: TimeButton = {
let button = TimeButton(type: .system)
button.setTitle("Morning", for: .normal)
button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
return button
}()
private lazy var afternoonButton: TimeButton = {
let button = TimeButton(type: .system)
button.setTitle("Afternoon", for: .normal)
button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
return button
}()
private lazy var eveningButton: TimeButton = {
let button = TimeButton(type: .system)
button.setTitle("Evening", for: .normal)
button.titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
return button
}()
// Switch Statement
#objc func timeButtonsTapped(_ sender: TimeButton) {
switch sender.tag {
case 10:
morningButton.backgroundColor = .darkPurpleTint
morningButton.setTitleColor(.white, for: .normal)
afternoonButton.backgroundColor = .white
afternoonButton.setTitleColor(.darkPurpleTint, for: .normal)
eveningButton.backgroundColor = .white
eveningButton.setTitleColor(.darkPurpleTint, for: .normal)
case 20:
afternoonButton.backgroundColor = .darkPurpleTint
afternoonButton.setTitleColor(.white, for: .normal)
morningButton.backgroundColor = .white
morningButton.setTitleColor(.darkPurpleTint, for: .normal)
eveningButton.backgroundColor = .white
eveningButton.setTitleColor(.darkPurpleTint, for: .normal)
case 30:
eveningButton.backgroundColor = .darkPurpleTint
eveningButton.setTitleColor(.white, for: .normal)
morningButton.backgroundColor = .white
morningButton.setTitleColor(.darkPurpleTint, for: .normal)
afternoonButton.backgroundColor = .white
afternoonButton.setTitleColor(.darkPurpleTint, for: .normal)
case 10 & 20:
morningButton.backgroundColor = .darkPurpleTint
morningButton.setTitleColor(.white, for: .normal)
afternoonButton.backgroundColor = .darkPurpleTint
afternoonButton.setTitleColor(.white, for: .normal)
eveningButton.backgroundColor = .white
eveningButton.setTitleColor(.darkPurpleTint, for: .normal)
case 10 & 30:
morningButton.backgroundColor = .darkPurpleTint
morningButton.setTitleColor(.white, for: .normal)
eveningButton.backgroundColor = .darkPurpleTint
eveningButton.setTitleColor(.white, for: .normal)
afternoonButton.backgroundColor = .white
afternoonButton.setTitleColor(.darkPurpleTint, for: .normal)
case 20 & 30:
afternoonButton.backgroundColor = .darkPurpleTint
afternoonButton.setTitleColor(.white, for: .normal)
eveningButton.backgroundColor = .darkPurpleTint
eveningButton.setTitleColor(.white, for: .normal)
morningButton.backgroundColor = .white
morningButton.setTitleColor(.darkPurpleTint, for: .normal)
case 10 & 20 & 30:
morningButton.backgroundColor = .darkPurpleTint
morningButton.setTitleColor(.white, for: .normal)
afternoonButton.backgroundColor = .darkPurpleTint
afternoonButton.setTitleColor(.white, for: .normal)
eveningButton.backgroundColor = .darkPurpleTint
eveningButton.setTitleColor(.white, for: .normal)
default:
morningButton.backgroundColor = .white
morningButton.setTitleColor(.darkPurpleTint, for: .normal)
afternoonButton.backgroundColor = .white
afternoonButton.setTitleColor(.darkPurpleTint, for: .normal)
eveningButton.backgroundColor = .white
eveningButton.setTitleColor(.darkPurpleTint, for: .normal)
}
}
// My Function
fileprivate func configureUI() {
morningButton.tag = 10
morningButton.isUserInteractionEnabled = true
afternoonButton.tag = 20
afternoonButton.isUserInteractionEnabled = true
eveningButton.tag = 30
eveningButton.isUserInteractionEnabled = true
morningButton.addTarget(self, action: #selector(timeButtonsTapped(_:)), for: .touchUpInside)
afternoonButton.addTarget(self, action: #selector(timeButtonsTapped(_:)), for: .touchUpInside)
eveningButton.addTarget(self, action: #selector(timeButtonsTapped(_:)), for: .touchUpInside)
}
You don't need to spread the button coloring logic all over your view controller. It results in lots of code duplication and poor maintainability in the future.
The UIButton has an isSelected property, you can use it in this particular case to save the state of a button. Also I would suggest moving all of the colors into the button's subclass so that the client doesn't have to worry about them. The button should be responsible for switching its colors.
Here's what the TimeButton class would look like:
class TimeButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
override var isSelected: Bool {
didSet {
updateBackgroundColor()
}
}
// MARK: - Private
private func setup() {
translatesAutoresizingMaskIntoConstraints = false
setTitleColor(.white, for: .normal)
setTitleColor(.systemPurple, for: .selected)
titleLabel?.font = UIFont(name: "AvenirNext-Medium", size: 14)
updateBackgroundColor()
}
private func updateBackgroundColor() {
backgroundColor = isSelected ? .white : .systemPurple
}
}
Your view controller now should look like this:
class ViewController: UIViewController {
private lazy var morningButton: TimeButton = {
let button = TimeButton(type: .custom)
button.setTitle("Morning", for: .normal)
button.addTarget(self, action: #selector(didSelectButton(_:)), for: .touchUpInside)
return button
}()
private lazy var afternoonButton: TimeButton = {
let button = TimeButton(type: .custom)
button.setTitle("Afternoon", for: .normal)
button.addTarget(self, action: #selector(didSelectButton(_:)), for: .touchUpInside)
return button
}()
private lazy var eveningButton: TimeButton = {
let button = TimeButton(type: .custom)
button.setTitle("Evening", for: .normal)
button.addTarget(self, action: #selector(didSelectButton(_:)), for: .touchUpInside)
return button
}()
#objc private func didSelectButton(_ button: TimeButton) {
button.isSelected = !button.isSelected
// do some additional stuff if needed
}
}
Note the type: .custom in the button's init - this will disable the default button styling when it gets selected.
If you want to get the value of some button - just refer to its isSelected property:
let shouldUseMorningTime = morningButton.isSelected

How to hide the letters on a .numberPad keyboard?

By default, when using a .numberPad keyboard on iPhone, the number keys also feature letters at the bottom of each key.
The letters are purely cosmetic, to help people with number input; but in my case (entering item quantities), they’re completely superfluous, maybe even confusing.
Is it possible to configure the keyboard to hide these letters?
Should I implement my own keyboard view just to properly present the keys?
I don’t believe there is currently a “digit only” keyboard without the text characters.
But you can create your own:
textField.inputView = NumericKeyboard(target: textField)
Where
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: UIKeyInput?
var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = $0
button.setTitle("\($0)", for: .normal)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.addTarget(self, action: #selector(didTapDigitButton(_:)), for: .touchUpInside)
return button
}
var deleteButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("⌫", for: .normal)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = "Delete"
button.addTarget(self, action: #selector(didTapDeleteButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput) {
self.target = target
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Actions
extension NumericKeyboard {
#objc func didTapDigitButton(_ sender: DigitButton) {
target?.insertText("\(sender.digit)")
}
#objc func didTapDeleteButton(_ sender: DigitButton) {
target?.deleteBackward()
}
}
// MARK: - Private initial configuration methods
private extension NumericKeyboard {
func configure() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
addButtons()
}
func addButtons() {
let stackView = createStackView(axis: .vertical)
stackView.frame = bounds
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(stackView)
for row in 0 ..< 3 {
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
for column in 0 ..< 3 {
subStackView.addArrangedSubview(numericButtons[row * 3 + column + 1])
}
}
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
let blank = UIView()
blank.layer.borderWidth = 0.5
blank.layer.borderColor = UIColor.darkGray.cgColor
subStackView.addArrangedSubview(blank)
subStackView.addArrangedSubview(numericButtons[0])
subStackView.addArrangedSubview(deleteButton)
}
func createStackView(axis: NSLayoutConstraint.Axis) -> UIStackView {
let stackView = UIStackView()
stackView.axis = axis
stackView.alignment = .fill
stackView.distribution = .fillEqually
return stackView
}
}
That yields:
Clearly, you can go nuts, customizing your keyboard to look however you’d like to. The above is fairly primitive, something I just dashed together. But it illustrates the idea: Make you own input view and use the UIKeyInput protocol to communicate keyboard input to the control.

UIButton Function add Tap Style

I Have written a function in Swift 4 which creates a button that receives the UIButton, a Color and a String for the title. I would like to add to this function a highlight or tap style but I am not sure how.
The function:
func homeBtn(button: UIButton, color: UIColor, title: String){
button.setTitle(title, for: .normal)
button.setTitleColor(color, for: .normal)
button.titleLabel?.font = UIFont(name: "Raleway-Medium", size: 24)
button.titleLabel?.lineBreakMode = .byWordWrapping
button.titleLabel?.textAlignment = .center
button.titleEdgeInsets = UIEdgeInsetsMake(20,20,20,20)
button.layer.borderColor = color.cgColor
button.layer.borderWidth = 2
button.layer.cornerRadius = button.frame.width/2
button.layer.backgroundColor = whiteColor.cgColor
}
The button is a circle and has a String centered in the middle with the UIColor being used as an outline. Can someone show me how to add a tap style to this function that uses the same UIColor to fill in the button and turn the text white please?
For the title color you can easily do this without adding events.
button.setTitleColor(.white, for: .highlighted)
button.setTitleColor(.black, for: .normal)
For the background color you need to define the following events:
button.addTarget(self, action: #selector(touchDown(button:)), for: .touchDown)
button.addTarget(self, action: #selector(touchUpInside(button:)), for: .touchUpInside)
Define functions - You can refer additional touch types as well - see UIControlEvents
#objc func touchDown(button: UIButton) {
guard let borderColor = button.layer.borderColor else {
return
}
button.backgroundColor = UIColor(cgColor: borderColor)
}
#objc func touchUpInside(button: UIButton) {
// Set backgroundColor for normal state...
}

Swift button created programmatically needs to have 2 lines for title

I am creating buttons programmatically like this:
let stage: Stages = stagesObjectsArray[i]
let button = UIButton(type: .Custom) as UIButton
button.setTitle(stage.name, forState: .Normal)
button.sizeToFit()
button.frame.origin = buttonPosition
let buttonIncrement = button.frame.size.width + padding.width
buttonPosition.x = buttonPosition.x + buttonIncrement
button.backgroundColor = UIColor.blackColor()
button.titleLabel?.font = UIFont(name: "", size: widthOfScreen/3.2)
button.setTitleColor(UIColor.darkGrayColor(), forState: UIControlState.Normal)
button.tag = stage.id
button.addTarget(self, action: #selector(GamesAllViewController.buttonPressedTopMenu(_:)), forControlEvents: .TouchUpInside)
buttonView.addSubview(button)
What I want to implement and can't figure out how is to have the title of the button on 2 rows if the width of the button becomes too big(for instance more than half the width of the screen).
I have found some questions on this topic, but they all assume they know the title of the button, while in my case it is dynamic and I do not know what it might be.
I have tried this code, but with no change:
button.titleLabel!.lineBreakMode = NSLineBreakMode.ByWordWrapping
button.titleLabel!.numberOfLines = 2
Can somebody help?
You can use "\n" and NoOFLines to 0
try this code
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
let button = UIButton(type: .Custom) as UIButton
button.setTitle("Line 1\nLine 2", forState: .Normal)
button.sizeToFit()
button.titleLabel!.lineBreakMode = NSLineBreakMode.ByWordWrapping
button.titleLabel!.numberOfLines = 0
button.titleLabel!.textAlignment = NSTextAlignment.Center
button.frame = CGRectMake(20, 100, 280, 40)
button.titleLabel?.textColor = UIColor.redColor()
button.backgroundColor = UIColor.blackColor()
button.addTarget(self, action: "methodName:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}

Resources