Implement dialpad view controller

This commit is contained in:
ismailgulek
2021-01-13 21:51:47 +03:00
parent 313ff48feb
commit aa6f3e8e60
7 changed files with 853 additions and 155 deletions
@@ -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
}
}