财神 2.2.1

财神 2.2.1

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2019年3月
SPM支持 SPM

Chris JonesDaniel Vancura 维护。



财神 2.2.1

  • Daniel Vancura

Caishen

Travis build status Cocoapods Compatible Carthage compatible Platform Docs

描述

Caishen 提供了一个易于使用的文本框,用于向用户请求付款卡信息并验证输入。它与 PaymentKit 有类似的功能,但它是一个完全用 Swift 编写的独立框架。Caishen 还可以轻松集成到其他第三方框架中,例如 CardIO

![Caishen 示例](caishen_example.gif)

要求

  • iOS 8.0+
  • Xcode 10.0+

安装

CocoaPods

Caishen 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中

pod "Caishen"

卡提亚(Carthage)

卡提亚可以通过GitHub上的卡提亚获取。要安装它,只需将以下行添加到您的Cartfile文件中

github "prolificinteractive/Caishen"

使用方法

示例

要运行示例项目,请克隆仓库,然后从Example目录首先运行pod install


在您的项目中

要将用于输入卡信息的文本字段添加到视图中,可以...

  • ... 在InterfaceBuilder视图中添加一个UITextField并将其类更改为CardTextField(当使用InterfaceBuilder时)
  • ... 或者使用其中一种初始化方法初始化CardTextField(当从代码实例化时)
    • init?(coder: aDecoder: NSCoder)
    • init(frame: CGRect)

要获取有关输入的卡信息的更新,请确认遵守协议CardTextFieldDelegate,并将视图控制器设置为文本字段的cardTextFieldDelegate

class MyViewController: UIViewController, CardTextFieldDelegate {
	
	@IBOutlet weak var cardTextField: CardTextField?
	
	override func viewDidLoad() {
		cardTextField?.cardTextFieldDelegate = self
		
		...
	}
	
	func cardTextField(_ cardTextField: CardTextField, didEnterCardInformation information: Card, withValidationResult validationResult: CardValidationResult) {
        // A valid card has been entered, if validationResult == CardValidationResult.Valid
    }
    
    func cardTextFieldShouldShowAccessoryImage(_ cardTextField: CardTextField) -> UIImage? {
        // You can return an image which will be used on cardTextField's accessory button
		 // If you return nil but provide an accessory button action, the unicode character "⇤" is displayed instead of an image to indicate an action that affects the text field.
    }
    
    func cardTextFieldShouldProvideAccessoryAction(_ cardTextField: CardTextField) -> (() -> ())? {
		 // You can return a callback function which will be called if a user tapped on cardTextField's accessory button
		 // If you return nil, cardTextField won't display an accessory button at all.
    }
	
	...

自定义文本字段外观

CardTextField与大多数UITextField一样可以进行自定义。为CardTextField设置以下标准属性(无论是通过代码还是从界面构建器)将影响文本字段,就像影响任何其他UITextField一样

属性 类型 描述
placeholder String? 卡片号码的占位符。当使用卡片号码作为占位符时,请确保格式正确,以便它使用为文本字段设置的cardNumberSeparator(例如,当使用" - "作为分隔符时,设置一个占位符,如"1234 - 1234 - 1234 - 1234")。
textColor UIColor? CardTextField中输入的文字颜色。
backgroundColor UIColor? 文本字段的背景颜色。
font UIFont? 输入文本的字体。
secureTextEntry 布尔 当设置为true时,文本字段中的任何输入都会加密(即用"•"字符遮盖)。
keyboardAppearance UIKeyboardAppearance 在文本字段中编辑文本时的键盘外观。
borderStyle UITextBorderStyle 文本字段的边框样式。

此外,CardTextField提供了针对其用途的属性(也可通过界面构建器访问)。

属性 类型 描述
cardNumberSeparator String? 用于分隔卡号中组的字符串。默认为" - "。
viewAnimationDuration Double? 在卡号文本字段和细节(月份、视图和cvv文本字段)之间切换时,视图动画的持续时间(以秒为单位)。
invalidInputColor UIColor? 无效输入的文本颜色。当输入无效的卡号时,文本将在此颜色下闪烁,如果卡已过期,则有效期也将以此颜色显示。

CardIO

CardIO可能是让用户输入支付卡信息的强有力工具之一。它使用相机,并允许用户扫描其信用卡。然而,您可能仍然想要为用户提供一个视觉吸引力强的文本字段来输入支付卡信息,因为用户可能想要限制对相机的访问,或者只是想手动输入这些信息。

为了为用户提供CardIO的链接,您可以使用CardTextField的prefillCardInformation方法以及之前提到的辅助按钮

// 1. Let your view controller confirm to the CardTextFieldDelegate and CardIOPaymentViewControllerDelegate protocol:
class ViewController: UIViewController, CardTextFieldDelegate, CardIOPaymentViewControllerDelegate {
	...
	
	// MARK: - CardTextFieldDelegate
    func cardTextField(_ cardTextField: CardTextField, didEnterCardInformation information: Card, withValidationResult validationResult: CardValidationResult) {
        if validationResult == .Valid {
        	// A valid payment card has been manually entered or CardIO was used to scan one.
        }
    }
    
    // 2. Optionally provide an image for the CardIO button
    func cardTextFieldShouldShowAccessoryImage(_ cardTextField: CardTextField) -> UIImage? {
        return UIImage(named: "cardIOIcon")
    }
    
    // 3. Set the action for the accessoryButton to open CardIO:
    func cardTextFieldShouldProvideAccessoryAction(_ cardTextField: CardTextField) -> (() -> ())? {
        return { [weak self] _ in
            let cardIOViewController = CardIOPaymentViewController(paymentDelegate: self)
            self?.presentViewController(cardIOViewController, animated: true, completion: nil)
        }
    }
    
    // MARK: - CardIOPaymentViewControllerDelegate
    
    // 4. When receiving payment card information from CardIO, prefill the text field with that information:
    func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
        cardTextField.prefillCardInformation(
        	cardInfo.cardNumber, 
        	month: Int(cardInfo.expiryMonth), 
        	year: Int(cardInfo.expiryYear), 
        	cvc: cardInfo.cvv)
        	
        paymentViewController.dismissViewControllerAnimated(true, completion: nil)
    }
    
    func userDidCancelPaymentViewController(paymentViewController: CardIOPaymentViewController!) {
        paymentViewController.dismissViewControllerAnimated(true, completion: nil)
    }
}

指定自己的卡类型

CardTextField还包含一个CardTypeRegister,它维护一组受此文本字段接受的卡类型。您可以创建自己的卡类型,并将它们添加到或从卡号文本字段中添加或删除。

struct MyCardType: CardType {
    
    // MARK: - Required

    // The name of your specified card type:
    public let name = "My Card Type"

    // Note: The image that will be displayed in the card number text field's image view when this card type has been detected will load from an asset with the name `cardType.name`.

    // If the Issuer Identification Number (the first six digits of the entered card number) of a card number 
    // starts with anything from 1000 to 1111, the card is identified as being of type "MyCardType":
    public let identifyingDigits = Set(1000...1111)

    // The number of digits expected in the Card Validation Code.
    public let CVCLength = 4

    // MARK: - Optional

    // The grouping of your card number type. The following results in a card number format
    // like "100 - 0000 - 00000 - 000000":
    // Not specifying this will result in a 16 digit number, separated into 4 groups of 4 digits.
    public let numberGrouping = [3, 4, 5, 6]

    /** 
     A boolean flag that indicates whether CVC validation is required for this card type or not.
     Setting this value to false will hide the CVC text field from the `CardTextField` and remove the required validation routine. The default is true.
     */
    public let requiresCVC = true

    /**
     A boolean flag that indicates whether expiry validation is required for this card type or not.
     Setting this value to false will hide the month and year text field from the `CardTextField` and remove the required
     validation routine. The default is true.
     */
    public let requiresExpiry = true
	
    public init() {
		
    }
}

...

class MyViewController: UIViewController, CardTextFieldDelegate {
	
	@IBOutlet weak var cardTextField: CardTextField?
	...
	
	func viewDidLoad() {
		cardTextField?.cardTypeRegister.registerCardType(MyCardType())
	}
	
	...
}

单独使用文本字段的组件

不需要在一个单独的文本字段中输入卡号、到期日和CVC,可以使用单独的文本字段来分别输入这些信息。

class ViewController: UIViewController, NumberInputTextFieldDelegate, CardInfoTextFieldDelegate {
    
    @IBOutlet weak var cardNumberTextField: NumberInputTextField!
    @IBOutlet weak var monthInputTextField: MonthInputTextField!
    @IBOutlet weak var yearInputTextField: YearInputTextField!
    @IBOutlet weak var cvcInputTextField: CVCInputTextField!
        
    // A card that is not nil when valid information has been entered in the text fields:
    var card: Card? {
        let number = cardNumberTextField.cardNumber
        let cvc = CVC(rawValue: cvcInputTextField.text ?? "")
        let expiry = Expiry(month: monthInputTextField.text ?? "", year: yearInputTextField.text ?? "")
                        ?? Expiry.invalid
        
        let cardType = cardNumberTextField.cardTypeRegister.cardTypeFor(number: cardNumberTextField.cardNumber)
        if cardType.validate(cvc: cvc).union(cardType.validate(expiry: expiry)).union(cardType.validate(number: number)) == .Valid {
            return Card(number: number, cvc: cvc, expiry: expiry)
        } else {
            return nil
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        cardNumberTextField.numberInputTextFieldDelegate = self
        monthInputTextField.cardInfoTextFieldDelegate = self
        yearInputTextField.cardInfoTextFieldDelegate = self
        cvcInputTextField.cardInfoTextFieldDelegate = self
        
        // Set the `deleteBackwardCallbacks` - closures which are called whenever a user hits
        // backspace on an empty text field.
        monthInputTextField.deleteBackwardCallback = { _ in self.cardNumberTextField.becomeFirstResponder() }
        yearInputTextField.deleteBackwardCallback = { _ in self.monthInputTextField.becomeFirstResponder() }
        cvcInputTextField.deleteBackwardCallback = { _ in self.yearInputTextField.becomeFirstResponder() }
    }
    
    func numberInputTextFieldDidComplete(_ numberInputTextField: NumberInputTextField) {
        cvcInputTextField.cardType = numberInputTextField.cardTypeRegister.cardTypeFor(number: numberInputTextField.cardNumber)
        print("Card number: \(numberInputTextField.cardNumber)")
        print(card)
        monthInputTextField.becomeFirstResponder()
    }
    
    func numberInputTextFieldDidChangeText(_ numberInputTextField: NumberInputTextField) {
        
    }
    
    func textField(_ textField: UITextField, didEnterValidInfo: String) {
        switch textField {
        case is MonthInputTextField:
            print("Month: \(didEnterValidInfo)")
            yearInputTextField.becomeFirstResponder()
        case is YearInputTextField:
            print("Year: \(didEnterValidInfo)")
            cvcInputTextField.becomeFirstResponder()
        case is CVCInputTextField:
            print("CVC: \(didEnterValidInfo)")
        default:
            break
        }
        print(card)
    }
    
    func textField(_ textField: UITextField, didEnterPartiallyValidInfo: String) {
        // The user entered information that is not valid but might become valid on further input.
        // Example: Entering "1" for the CVC is partially valid, while entering "a" is not.
    }
    
    func textField(_ textField: UITextField, didEnterOverflowInfo overFlowDigits: String) {
        // This function is used in a CardTextField to carry digits to the next text field.
        // Example: A user entered "02/20" as expiry and now tries to append "5" to the month.
        //          On a card text field, the year will be replaced with "5" - the overflow digit.
    }
    
    // ...
}

为Caishen做出贡献

要报告一个错误或增强请求,请随意在该部分下提交一个问题。

如果您想为项目做出贡献,请从这个仓库进行Fork并提交一个Pull Request。代码贡献应遵循《Prolific Swift风格指南》中指定的标准。

许可协议

菜神(Caishen)版权所有(C) 2017 Prolific Interactive。它可以在《LICENSE》文件中指定的条款下重新分发。

维护者

prolific

菜神(Caishen)由Prolific Interactive维护和赞助。名称和标志是Prolific Interactive的商标。