mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
Implement dialpad view controller
This commit is contained in:
@@ -17,27 +17,64 @@
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import libPhoneNumber_iOS
|
||||
|
||||
protocol DialpadViewControllerDelegate: class {
|
||||
func dialpadViewControllerDidTapSetupAction(_ viewController: DialpadViewController)
|
||||
func dialpadViewControllerDidCancel(_ viewController: DialpadViewController)
|
||||
@objc protocol DialpadViewControllerDelegate: class {
|
||||
func dialpadViewControllerDidTapCall(_ viewController: DialpadViewController, withPhoneNumber phoneNumber: String)
|
||||
func dialpadViewControllerDidTapClose(_ viewController: DialpadViewController)
|
||||
}
|
||||
|
||||
final class DialpadViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
@objcMembers
|
||||
class DialpadViewController: UIViewController {
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var logoImageView: UIImageView!
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var okButtonBackgroundView: UIView!
|
||||
@IBOutlet private weak var okButton: UIButton!
|
||||
@IBOutlet private weak var phoneNumberTextField: UITextField! {
|
||||
didSet {
|
||||
phoneNumberTextField.text = nil
|
||||
// avoid showing keyboard on text field
|
||||
phoneNumberTextField.inputView = UIView()
|
||||
phoneNumberTextField.inputAccessoryView = UIView()
|
||||
}
|
||||
}
|
||||
@IBOutlet private weak var lineView: UIView!
|
||||
@IBOutlet private weak var digitsStackView: UIStackView!
|
||||
@IBOutlet private weak var backspaceButton: DialpadActionButton! {
|
||||
didSet {
|
||||
backspaceButton.type = .backspace
|
||||
}
|
||||
}
|
||||
@IBOutlet private weak var callButton: DialpadActionButton! {
|
||||
didSet {
|
||||
callButton.type = .call
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private enum Constants {
|
||||
static let sizeOniPad: CGSize = CGSize(width: 375, height: 667)
|
||||
}
|
||||
|
||||
private var wasCursorAtTheEnd: Bool = true
|
||||
|
||||
/// Phone number as formatted
|
||||
private var phoneNumber: String = "" {
|
||||
willSet {
|
||||
wasCursorAtTheEnd = isCursorAtTheEnd()
|
||||
} didSet {
|
||||
phoneNumberTextField.text = phoneNumber
|
||||
if wasCursorAtTheEnd {
|
||||
moveCursorToTheEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Phone number as non-formatted
|
||||
private var rawPhoneNumber: String {
|
||||
return phoneNumber.vc_removingAllWhitespaces()
|
||||
}
|
||||
private var theme: Theme!
|
||||
|
||||
// MARK: Public
|
||||
@@ -59,10 +96,7 @@ final class DialpadViewController: UIViewController {
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = "Template"
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.setupViews()
|
||||
titleLabel.text = VectorL10n.dialpadTitle
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
@@ -73,38 +107,73 @@ final class DialpadViewController: UIViewController {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupViews() {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
self?.cancelButtonAction()
|
||||
private func isCursorAtTheEnd() -> Bool {
|
||||
guard let selectedRange = phoneNumberTextField.selectedTextRange else {
|
||||
return true
|
||||
}
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
|
||||
// let logoImage = Asset.Images.*
|
||||
// self.logoImageView.image = keybackupLogoImage
|
||||
|
||||
// self.titleLabel.text = VectorL10n.xxxxTitle
|
||||
// self.informationLabel.text = VectorL10n.xxxxDescription
|
||||
//
|
||||
// self.okButton.setTitle(VectorL10n.xxxxAction, for: .normal)
|
||||
if !selectedRange.isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
let cursorEndPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.end)
|
||||
|
||||
return cursorEndPos == phoneNumber.count
|
||||
}
|
||||
|
||||
private func moveCursorToTheEnd() {
|
||||
guard let cursorPos = phoneNumberTextField.position(from: phoneNumberTextField.beginningOfDocument,
|
||||
offset: phoneNumber.count) else { return }
|
||||
|
||||
phoneNumberTextField.selectedTextRange = phoneNumberTextField.textRange(from: cursorPos,
|
||||
to: cursorPos)
|
||||
}
|
||||
|
||||
private func reformatPhoneNumber() {
|
||||
guard let phoneNumberUtil = NBPhoneNumberUtil.sharedInstance() else {
|
||||
// no formatter
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
// try formatting the number
|
||||
if phoneNumber.hasPrefix("00") {
|
||||
let range = phoneNumber.startIndex..<phoneNumber.index(phoneNumber.startIndex, offsetBy: 2)
|
||||
phoneNumber.replaceSubrange(range, with: "+")
|
||||
}
|
||||
let nbPhoneNumber = try phoneNumberUtil.parse(rawPhoneNumber, defaultRegion: nil)
|
||||
phoneNumber = try phoneNumberUtil.format(nbPhoneNumber, numberFormat: .INTERNATIONAL)
|
||||
} catch {
|
||||
// continue without formatting
|
||||
}
|
||||
}
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
self.view.backgroundColor = theme.backgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.logoImageView.tintColor = theme.textPrimaryColor
|
||||
titleLabel.textColor = theme.noticeSecondaryColor
|
||||
phoneNumberTextField.textColor = theme.textPrimaryColor
|
||||
lineView.backgroundColor = theme.lineBreakColor
|
||||
closeButton.setBackgroundImage(Asset.Images.closeButton.image.vc_tintedImage(usingColor: theme.tabBarUnselectedItemTintColor), for: .normal)
|
||||
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.okButtonBackgroundView.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.okButton)
|
||||
updateThemesOfAllButtons(in: digitsStackView, with: theme)
|
||||
}
|
||||
|
||||
private func updateThemesOfAllButtons(in view: UIView, with theme: Theme) {
|
||||
if let button = view as? DialpadButton {
|
||||
button.update(theme: theme)
|
||||
} else {
|
||||
for subview in view.subviews {
|
||||
updateThemesOfAllButtons(in: subview, with: theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
@@ -116,11 +185,90 @@ final class DialpadViewController: UIViewController {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
@IBAction private func validateButtonAction(_ sender: Any) {
|
||||
self.delegate?.dialpadViewControllerDidTapSetupAction(self)
|
||||
@IBAction private func closeButtonAction(_ sender: UIButton) {
|
||||
delegate?.dialpadViewControllerDidTapClose(self)
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
self.delegate?.dialpadViewControllerDidCancel(self)
|
||||
|
||||
@IBAction private func digitButtonAction(_ sender: DialpadButton) {
|
||||
let digit = sender.title(for: .normal) ?? ""
|
||||
|
||||
if let selectedRange = phoneNumberTextField.selectedTextRange {
|
||||
if isCursorAtTheEnd() {
|
||||
phoneNumber += digit
|
||||
reformatPhoneNumber()
|
||||
return
|
||||
}
|
||||
let cursorStartPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.start)
|
||||
let cursorEndPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.end)
|
||||
|
||||
phoneNumber.replaceSubrange((phoneNumber.index(phoneNumber.startIndex, offsetBy: cursorStartPos))..<(phoneNumber.index(phoneNumber.startIndex, offsetBy: cursorEndPos)), with: digit)
|
||||
|
||||
guard let cursorPos = phoneNumberTextField.position(from: phoneNumberTextField.beginningOfDocument,
|
||||
offset: cursorEndPos + digit.count) else { return }
|
||||
|
||||
reformatPhoneNumber()
|
||||
|
||||
phoneNumberTextField.selectedTextRange = phoneNumberTextField.textRange(from: cursorPos,
|
||||
to: cursorPos)
|
||||
} else {
|
||||
phoneNumber += digit
|
||||
reformatPhoneNumber()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func backspaceButtonAction(_ sender: DialpadActionButton) {
|
||||
if phoneNumber.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
if let selectedRange = phoneNumberTextField.selectedTextRange {
|
||||
let cursorStartPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.start)
|
||||
let cursorEndPos = phoneNumberTextField.offset(from: phoneNumberTextField.beginningOfDocument, to: selectedRange.end)
|
||||
|
||||
let rangePos: UITextPosition!
|
||||
|
||||
if selectedRange.isEmpty {
|
||||
// just caret, remove one char from the cursor position
|
||||
if cursorStartPos == 0 {
|
||||
// already at the beginning of the text, no more text to remove here
|
||||
return
|
||||
}
|
||||
phoneNumber.replaceSubrange((phoneNumber.index(phoneNumber.startIndex, offsetBy: cursorStartPos-1))..<(phoneNumber.index(phoneNumber.startIndex, offsetBy: cursorEndPos)), with: "")
|
||||
|
||||
rangePos = phoneNumberTextField.position(from: phoneNumberTextField.beginningOfDocument,
|
||||
offset: cursorStartPos-1)
|
||||
} else {
|
||||
// really some text selected, remove selected range of text
|
||||
|
||||
phoneNumber.replaceSubrange((phoneNumber.index(phoneNumber.startIndex, offsetBy: cursorStartPos))..<(phoneNumber.index(phoneNumber.startIndex, offsetBy: cursorEndPos)), with: "")
|
||||
|
||||
rangePos = phoneNumberTextField.position(from: phoneNumberTextField.beginningOfDocument,
|
||||
offset: cursorStartPos)
|
||||
}
|
||||
|
||||
reformatPhoneNumber()
|
||||
|
||||
guard let cursorPos = rangePos else { return }
|
||||
phoneNumberTextField.selectedTextRange = phoneNumberTextField.textRange(from: cursorPos,
|
||||
to: cursorPos)
|
||||
} else {
|
||||
phoneNumber.removeLast()
|
||||
reformatPhoneNumber()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func callButtonAction(_ sender: DialpadActionButton) {
|
||||
delegate?.dialpadViewControllerDidTapCall(self, withPhoneNumber: rawPhoneNumber)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - CustomSizedPresentable
|
||||
|
||||
extension DialpadViewController: CustomSizedPresentable {
|
||||
|
||||
func customSize(withParentContainerSize containerSize: CGSize) -> CGSize {
|
||||
return Constants.sizeOniPad
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user