Skip to content

Commit

Permalink
Merge pull request #7 from BadhanGanesh/develop
Browse files Browse the repository at this point in the history
Added Haptics and Modal dismissal alert
  • Loading branch information
BadhanGanesh authored May 28, 2020
2 parents 9382e48 + a8eba01 commit b86c32b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 16 deletions.
85 changes: 70 additions & 15 deletions Sources/BJOTPViewController/BJOTPViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ open class BJOTPViewController: UIViewController {

////////////////////////////////////////////////////////////////
//MARK:-
//MARK:Private Properties
//MARK: Private Properties
//MARK:-
////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -102,7 +102,7 @@ open class BJOTPViewController: UIViewController {
allTextFields[idx].text = String(element)
}
self.touchesEnded(Set.init(arrayLiteral: UITouch()), with: nil)
self.delegate?.authenticate(stringToPaste, from: self)
self.informDelegate(stringToPaste, from: self)
}
}
}
Expand Down Expand Up @@ -238,6 +238,15 @@ open class BJOTPViewController: UIViewController {
*/
@objc public var shouldAutomaticallyPasteCopiedStringFromClipboard: Bool = false

/**
* Uses haptics for touches, interactions, successes and errors within the OTP view controller.
*
* Default is `true`.
*
* - Author: Badhan Ganesh
*/
@objc public var hapticsEnabled: Bool = true


////////////////////////////////////////////////////////////////
//MARK:-
Expand All @@ -262,7 +271,7 @@ open class BJOTPViewController: UIViewController {
public override func viewDidLoad() {
super.viewDidLoad()
self.constructUI()
self.configureKeyboardAndOtherNotifications()
self.initialConfiguration()
}

public override func viewDidAppear(_ animated: Bool) {
Expand All @@ -276,14 +285,17 @@ open class BJOTPViewController: UIViewController {
otpString += textField.text!
return (textField.text ?? "") == "" ? emptyTextsCount + 1 : emptyTextsCount
})
if numberOfEmptyTextFields > 0 { return }
if numberOfEmptyTextFields > 0 {
UINotificationFeedbackGenerator().notificationOccurred(.error)
return
}
self.view.endEditing(true)
self.delegate?.authenticate(otpString, from: self)
self.informDelegate(otpString, from: self)
}

@objc private func closeButtonTapped(_ sender: UIButton) {
if self.navigationController == nil {
self.dismiss(animated: true, completion: nil)
self.askUserConsentBeforeDismissingModal()
}
}

Expand Down Expand Up @@ -320,6 +332,7 @@ open class BJOTPViewController: UIViewController {
}

deinit {
print("**********\nBJOTPViewController deinit being called\n**********")
self.removeListeners()
self.allTextFields.removeAll()
self.textFieldsIndexes.removeAll()
Expand Down Expand Up @@ -375,7 +388,7 @@ extension BJOTPViewController: UITextFieldDelegate {
}
textField.resignFirstResponder()
self.touchesEnded(Set.init(arrayLiteral: UITouch()), with: nil)
self.delegate?.authenticate(string, from: self)
self.informDelegate(string, from: self)
///If the replacing string is of 1 character length, then we just allow it to be replaced
///and set the responder to be the next text field
} else if string.count == 1 {
Expand Down Expand Up @@ -407,7 +420,7 @@ extension BJOTPViewController: UITextFieldDelegate {
allTextFields[idx].text = otpChar
}
self.touchesEnded(Set.init(arrayLiteral: UITouch()), with: nil)
self.delegate?.authenticate(finalOTP, from: self)
self.informDelegate(finalOTP, from: self)
isAutoFillingFromSMS = false
autoFillBuffer.removeAll()
}
Expand Down Expand Up @@ -458,8 +471,8 @@ extension BJOTPViewController: UITextFieldDelegate {
return (textField.text ?? "").isEmpty ? emptyTextsCount + 1 : emptyTextsCount
})
if numberOfEmptyTextFields > 0 { return }
if let delegate = delegate {
delegate.authenticate(otpString, from: self)
if let _ = delegate {
self.informDelegate(otpString, from: self)
} else {
fatalError("Delegate is nil in BJTOPViewController.")
}
Expand Down Expand Up @@ -589,7 +602,7 @@ extension BJOTPViewController {

fileprivate func layoutHeadingLabel() {

if (self.navigationController?.isNavigationBarHidden ?? true) {
if self.navigationController?.isNavigationBarHidden ?? true {

let headingTitle = UILabel()
headingTitle.tarmic = false
Expand Down Expand Up @@ -668,13 +681,14 @@ extension BJOTPViewController {

let captionFontMetric = UIFontMetrics.init(forTextStyle: .caption2)
let footerLabelFont = captionFontMetric.scaledFont(for: .systemFont(ofSize: 9, weight: .regular))

footerLabel.font = footerLabelFont

if #available(iOS 13.0, *) {
footerLabel.textColor = UIColor.secondaryLabel.withAlphaComponent(0.4)
} else {
footerLabel.textColor = UIColor(red: 0.23529411764705882, green: 0.23529411764705882, blue: 0.2627450980392157, alpha: 0.6).withAlphaComponent(0.4)
}

footerLabel.setContentHuggingPriority(.init(1000), for: .vertical)
footerLabel.setContentCompressionResistancePriority(.init(1000), for: .vertical)
footerLabel.lineBreakMode = .byTruncatingMiddle
Expand All @@ -696,9 +710,10 @@ extension BJOTPViewController {

fileprivate func layoutAuthenticateButtonWith(sibling view: UIView) {

let authenticateButton = BJOTPAuthenticateButton.init()
let authenticateButton = BJOTPAuthenticateButton()
authenticateButton.tarmic = false
authenticateButton.roundCorners(amount: 6.0)
authenticateButton.useHaptics = self.hapticsEnabled
authenticateButton.setTitle(self.authenticateButtonTitle, for: .normal)

let authenticateButtonFontMetric = UIFontMetrics.init(forTextStyle: .headline)
Expand Down Expand Up @@ -750,7 +765,8 @@ extension BJOTPViewController {
}

fileprivate func offsetValueDuringRest() -> CGFloat {
return (self.navigationController != nil) ? (self.navBarHeight + NSObject.statusBarHeight) / 2 : NSObject.statusBarHeightOffset

return (!(self.navigationController?.isNavigationBarHidden ?? true)) ? (self.navBarHeight + NSObject.statusBarHeight) / 2 : NSObject.statusBarHeightOffset
}

}
Expand All @@ -765,6 +781,20 @@ extension BJOTPViewController {

extension BJOTPViewController {

fileprivate func initialConfiguration() {
self.modalConfig()
self.configureKeyboardAndOtherNotifications()
}

fileprivate func modalConfig() {
if self.navigationController == nil {
if #available(iOS 13.0, *) {
self.isModalInPresentation = true
self.presentationController?.delegate = self
}
}
}

fileprivate func configureKeyboardAndOtherNotifications() {
#if swift(>=5.0)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
Expand Down Expand Up @@ -929,14 +959,33 @@ extension BJOTPViewController {
return
}
if shouldPromptUserToPasteCopiedStringFromClipboard {
self.showSimpleAlertWithTitle("Do you want to paste the copied text and proceed?", firstButtonTitle: "No", secondButtonTitle: "Yes") { (secondButtonAction) in
UINotificationFeedbackGenerator().notificationOccurred(.success)
self.showSimpleAlertWithTitle("Do you want to paste the text from clipboard and proceed?", firstButtonTitle: "No", secondButtonTitle: "Yes") { (secondButtonAction) in
self.stringToPaste = clipboardString!
}
}
}
}
}

/**
* Use this method to inform the delegate that a valid OTP has been entered.
*
* This method can be useful if you want to prepend or appennd anything in success scenarios.
*
* - Author: Badhan Ganesh
*/
private func informDelegate(_ otp: String, from viewController: BJOTPViewController) {
self.delegate?.authenticate(otp, from: viewController)
}

private func askUserConsentBeforeDismissingModal() {
if hapticsEnabled { UINotificationFeedbackGenerator().notificationOccurred(.error) }
self.showSimpleAlertWithTitle("Are you sure you want to close without authenticating?", message: nil, firstButtonTitle: "No", secondButtonTitle: "Yes", isSecondButtonDestructive: true, firstButtonAction: nil) { (action) in
self.dismiss(animated: true, completion: nil)
}
}

private func removeListeners() {
#if swift(>=5.0)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
Expand All @@ -949,3 +998,9 @@ extension BJOTPViewController {
#endif
}
}

extension BJOTPViewController: UIAdaptivePresentationControllerDelegate {
public func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
self.askUserConsentBeforeDismissingModal()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import UIKit

final class BJOTPAuthenticateButton: UIButton {

var useHaptics: Bool = true

init() {
super.init(frame: .zero)
self.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .semibold).normalized()
Expand All @@ -44,6 +46,7 @@ final class BJOTPAuthenticateButton: UIButton {
self.transform = .init(scaleX: 0.97, y: 0.98)
self.alpha = 0.90
}
self.generateHaptic()
super.touchesBegan(touches, with: event)
}

Expand All @@ -52,6 +55,7 @@ final class BJOTPAuthenticateButton: UIButton {
self.transform = .identity
self.alpha = 1.0
}
self.generateHaptic()
super.touchesEnded(touches, with: event)
}

Expand All @@ -60,7 +64,14 @@ final class BJOTPAuthenticateButton: UIButton {
self.transform = .identity
self.alpha = 1.0
}
self.generateHaptic()
super.touchesCancelled(touches, with: event)
}

private func generateHaptic() {
if (self.useHaptics) {
UISelectionFeedbackGenerator().selectionChanged()
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ extension UIViewController {
@objc func showSimpleAlertWithTitle(_ title: String? = nil, message: String? = nil,
firstButtonTitle: String? = nil,
secondButtonTitle: String? = nil,
isSecondButtonDestructive: Bool = false,
firstButtonAction: ((UIAlertAction) -> Void)? = nil,
secondButtonAction: ((UIAlertAction) -> Void)? = nil) {

Expand All @@ -53,7 +54,7 @@ extension UIViewController {
}

if let st = secondButtonTitle {
let secondAction = UIAlertAction.init(title: st, style: .default, handler: secondButtonAction)
let secondAction = UIAlertAction.init(title: st, style: isSecondButtonDestructive ? .destructive : .default, handler: secondButtonAction)
alert.addAction(secondAction)
}

Expand Down

0 comments on commit b86c32b

Please sign in to comment.