无法在现有的tableviewcontroller上显示.xib viewcontroller

时间:2017-09-26 23:33:09

标签: ios iphone swift swift3

我在我的应用程序的日志功能中使用了github的PasscodeLock swift源代码。我使用密码允许用户锁定日记,以便人们无法打开应用程序并阅读他们的条目。

我项目中的.xib文件是应该显示的实际密码锁定屏幕。附加的PasscodeLockPresenter.swift文件是负责在现有窗口上呈现视图控制器并呈现锁定屏幕的文件。锁定屏幕也启用了触摸ID。当我进入应用程序的屏幕时应该显示锁定屏幕时,会出现Touch ID提示,但没有锁屏视图控制器。我该如何解决这个问题?这只是一个错误吗?

PasscodeLockPresenter.swift

wire.endTransmission

PasscodeLockViewController.swift

import UIKit

open class PasscodeLockPresenter {

    fileprivate var mainWindow: UIWindow?

    fileprivate lazy var passcodeLockWindow: UIWindow = {

        let window = UIWindow(frame: UIScreen.main.bounds)

        window.windowLevel = 0
        window.makeKeyAndVisible()

        return window
    }()

    fileprivate let passcodeConfiguration: PasscodeLockConfigurationType
    open var isPasscodePresented = false
    open let passcodeLockVC: PasscodeLockViewController

    public init(mainWindow window: UIWindow?, configuration: PasscodeLockConfigurationType, viewController: PasscodeLockViewController) {

        mainWindow = window
        mainWindow?.windowLevel = 1
        passcodeConfiguration = configuration

        passcodeLockVC = viewController
    }

    public convenience init(mainWindow window: UIWindow?, configuration: PasscodeLockConfigurationType) {

        let passcodeLockVC = PasscodeLockViewController(state: .enterPasscode, configuration: configuration)

        self.init(mainWindow: window, configuration: configuration, viewController: passcodeLockVC)
    }

    open func presentPasscodeLock() {

        guard passcodeConfiguration.repository.hasPasscode else { return }
        guard !isPasscodePresented else { return }

        isPasscodePresented = true

        passcodeLockWindow.windowLevel = 2
        passcodeLockWindow.isHidden = false

        mainWindow?.windowLevel = 1
        mainWindow?.endEditing(true)

        let passcodeLockVC = PasscodeLockViewController(state: .enterPasscode, configuration: passcodeConfiguration)
        let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback

        passcodeLockVC.dismissCompletionCallback = { [weak self] in

            userDismissCompletionCallback?()

            self?.dismissPasscodeLock()
        }

        passcodeLockWindow.rootViewController = passcodeLockVC
    }

    open func dismissPasscodeLock(animated: Bool = true) {

        isPasscodePresented = false
        mainWindow?.windowLevel = 1
        mainWindow?.makeKeyAndVisible()

        if animated {

            animatePasscodeLockDismissal()

        } else {

            passcodeLockWindow.windowLevel = 0
            passcodeLockWindow.rootViewController = nil
        }
    }

    internal func animatePasscodeLockDismissal() {

        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 1,
            initialSpringVelocity: 0,
            options: UIViewAnimationOptions(),
            animations: { [weak self] in

                self?.passcodeLockWindow.alpha = 0
            },
            completion: { [weak self] _ in

                self?.passcodeLockWindow.windowLevel = 0
                self?.passcodeLockWindow.rootViewController = nil
                self?.passcodeLockWindow.alpha = 1
            }
        )
    }
}

EnterPasscodeState.swift

import UIKit

open class PasscodeLockViewController: UIViewController, PasscodeLockTypeDelegate {

    public enum LockState {
        case enterPasscode
        case setPasscode
        case changePasscode
        case removePasscode

        func getState() -> PasscodeLockStateType {

            switch self {
            case .enterPasscode: return EnterPasscodeState()
            case .setPasscode: return SetPasscodeState()
            case .changePasscode: return ChangePasscodeState()
            case .removePasscode: return EnterPasscodeState(allowCancellation: true)
            }
        }
    }

    @IBOutlet open weak var titleLabel: UILabel?
    @IBOutlet open weak var descriptionLabel: UILabel?
    @IBOutlet open var placeholders: [PasscodeSignPlaceholderView] = [PasscodeSignPlaceholderView]()
    @IBOutlet open weak var cancelButton: UIButton?
    @IBOutlet open weak var deleteSignButton: UIButton?
    @IBOutlet open weak var touchIDButton: UIButton?
    @IBOutlet open weak var placeholdersX: NSLayoutConstraint?

    open var successCallback: ((_ lock: PasscodeLockType) -> Void)?
    open var dismissCompletionCallback: (()->Void)?
    open var animateOnDismiss: Bool
    open var notificationCenter: NotificationCenter?

    internal let passcodeConfiguration: PasscodeLockConfigurationType
    internal let passcodeLock: PasscodeLockType
    internal var isPlaceholdersAnimationCompleted = true

    fileprivate var shouldTryToAuthenticateWithBiometrics = true

    // MARK: - Initializers

    public init(state: PasscodeLockStateType, configuration: PasscodeLockConfigurationType, animateOnDismiss: Bool = true) {

        self.animateOnDismiss = animateOnDismiss

        passcodeConfiguration = configuration
        passcodeLock = PasscodeLock(state: state, configuration: configuration)

        let nibName = "PasscodeLockView"
        let bundle: Bundle = bundleForResource(nibName, ofType: "nib")

        super.init(nibName: nibName, bundle: bundle)

        passcodeLock.delegate = self
        notificationCenter = NotificationCenter.default
    }

    public convenience init(state: LockState, configuration: PasscodeLockConfigurationType, animateOnDismiss: Bool = true) {

        self.init(state: state.getState(), configuration: configuration, animateOnDismiss: animateOnDismiss)
    }

    public required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {

        clearEvents()
    }

    // MARK: - View

    open override func viewDidLoad() {
        super.viewDidLoad()

        updatePasscodeView()
        deleteSignButton?.isEnabled = false

        setupEvents()
    }

    open override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        if shouldTryToAuthenticateWithBiometrics {

            authenticateWithBiometrics()
        }
    }

    internal func updatePasscodeView() {

        titleLabel?.text = passcodeLock.state.title
        descriptionLabel?.text = passcodeLock.state.description
        cancelButton?.isHidden = !passcodeLock.state.isCancellableAction
        touchIDButton?.isHidden = !passcodeLock.isTouchIDAllowed
    }

    // MARK: - Events

    fileprivate func setupEvents() {

        notificationCenter?.addObserver(self, selector: #selector(PasscodeLockViewController.appWillEnterForegroundHandler(_:)), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        notificationCenter?.addObserver(self, selector: #selector(PasscodeLockViewController.appDidEnterBackgroundHandler(_:)), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
    }

    fileprivate func clearEvents() {

        notificationCenter?.removeObserver(self, name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        notificationCenter?.removeObserver(self, name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
    }

    open func appWillEnterForegroundHandler(_ notification: Notification) {

        authenticateWithBiometrics()
    }

    open func appDidEnterBackgroundHandler(_ notification: Notification) {

        shouldTryToAuthenticateWithBiometrics = false
    }

    // MARK: - Actions

    @IBAction func passcodeSignButtonTap(_ sender: PasscodeSignButton) {

        guard isPlaceholdersAnimationCompleted else { return }

        passcodeLock.addSign(sender.passcodeSign)
    }

    @IBAction func cancelButtonTap(_ sender: UIButton) {

        dismissPasscodeLock(passcodeLock)
    }

    @IBAction func deleteSignButtonTap(_ sender: UIButton) {

        passcodeLock.removeSign()
    }

    @IBAction func touchIDButtonTap(_ sender: UIButton) {

        passcodeLock.authenticateWithBiometrics()
    }

    fileprivate func authenticateWithBiometrics() {

        if passcodeConfiguration.shouldRequestTouchIDImmediately && passcodeLock.isTouchIDAllowed {

            passcodeLock.authenticateWithBiometrics()
        }
    }

    internal func dismissPasscodeLock(_ lock: PasscodeLockType, completionHandler: (() -> Void)? = nil) {

        // if presented as modal
        if presentingViewController?.presentedViewController == self {

            dismiss(animated: animateOnDismiss, completion: { [weak self] _ in

                self?.dismissCompletionCallback?()

                completionHandler?()
            })

            return

        // if pushed in a navigation controller
        } else if navigationController != nil {

            navigationController?.popViewController(animated: animateOnDismiss)
        }

        dismissCompletionCallback?()

        completionHandler?()
    }

    // MARK: - Animations

    internal func animateWrongPassword() {

        deleteSignButton?.isEnabled = false
        isPlaceholdersAnimationCompleted = false

        animatePlaceholders(placeholders, toState: .error)

        placeholdersX?.constant = -40
        view.layoutIfNeeded()

        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 0.2,
            initialSpringVelocity: 0,
            options: [],
            animations: {

                self.placeholdersX?.constant = 0
                self.view.layoutIfNeeded()
            },
            completion: { completed in

                self.isPlaceholdersAnimationCompleted = true
                self.animatePlaceholders(self.placeholders, toState: .inactive)
        })
    }

    internal func animatePlaceholders(_ placeholders: [PasscodeSignPlaceholderView], toState state: PasscodeSignPlaceholderView.State) {

        for placeholder in placeholders {

            placeholder.animateState(state)
        }
    }

    fileprivate func animatePlacehodlerAtIndex(_ index: Int, toState state: PasscodeSignPlaceholderView.State) {

        guard index < placeholders.count && index >= 0 else { return }

        placeholders[index].animateState(state)
    }

    // MARK: - PasscodeLockDelegate

    open func passcodeLockDidSucceed(_ lock: PasscodeLockType) {

        deleteSignButton?.isEnabled = true
        animatePlaceholders(placeholders, toState: .inactive)
        dismissPasscodeLock(lock, completionHandler: { [weak self] _ in
            self?.successCallback?(lock)
        })
    }

    open func passcodeLockDidFail(_ lock: PasscodeLockType) {

        animateWrongPassword()
    }

    open func passcodeLockDidChangeState(_ lock: PasscodeLockType) {

        updatePasscodeView()
        animatePlaceholders(placeholders, toState: .inactive)
        deleteSignButton?.isEnabled = false
    }

    open func passcodeLock(_ lock: PasscodeLockType, addedSignAtIndex index: Int) {

        animatePlacehodlerAtIndex(index, toState: .active)
        deleteSignButton?.isEnabled = true
    }

    open func passcodeLock(_ lock: PasscodeLockType, removedSignAtIndex index: Int) {

        animatePlacehodlerAtIndex(index, toState: .inactive)

        if index == 0 {

            deleteSignButton?.isEnabled = false
        }
    }
}

PasscodeLock.swift

import Foundation

public let PasscodeLockIncorrectPasscodeNotification = "passcode.lock.incorrect.passcode.notification"

struct EnterPasscodeState: PasscodeLockStateType {

    let title: String
    let description: String
    let isCancellableAction: Bool
    var isTouchIDAllowed = true

    fileprivate var inccorectPasscodeAttempts = 0
    fileprivate var isNotificationSent = false

    init(allowCancellation: Bool = false) {

        isCancellableAction = allowCancellation
        title = localizedStringFor("PasscodeLockEnterTitle", comment: "Enter passcode title")
        description = localizedStringFor("PasscodeLockEnterDescription", comment: "Enter passcode description")
    }

    mutating func acceptPasscode(_ passcode: [String], fromLock lock: PasscodeLockType) {

        guard let currentPasscode = lock.repository.passcode else {
            return
        }

        if passcode == currentPasscode {

            lock.delegate?.passcodeLockDidSucceed(lock)

        } else {

            inccorectPasscodeAttempts += 1

            if inccorectPasscodeAttempts >= lock.configuration.maximumInccorectPasscodeAttempts {

                postNotification()
            }

            lock.delegate?.passcodeLockDidFail(lock)
        }
    }

    fileprivate mutating func postNotification() {

        guard !isNotificationSent else { return }

        let center = NotificationCenter.default

        center.post(name: Notification.Name(rawValue: PasscodeLockIncorrectPasscodeNotification), object: nil)

        isNotificationSent = true
    }
}

非常感谢你!

另外,我在tableviewcontroller上使用此代码尝试显示.xib锁屏

import Foundation
import LocalAuthentication

open class PasscodeLock: PasscodeLockType {

    open weak var delegate: PasscodeLockTypeDelegate?
    open let configuration: PasscodeLockConfigurationType

    open var repository: PasscodeRepositoryType {
        return configuration.repository
    }

    open var state: PasscodeLockStateType {
        return lockState
    }

    open var isTouchIDAllowed: Bool {
        return isTouchIDEnabled() && configuration.isTouchIDAllowed && lockState.isTouchIDAllowed
    }

    fileprivate var lockState: PasscodeLockStateType
    fileprivate lazy var passcode = [String]()

    public init(state: PasscodeLockStateType, configuration: PasscodeLockConfigurationType) {

        precondition(configuration.passcodeLength > 0, "Passcode length sould be greather than zero.")

        self.lockState = state
        self.configuration = configuration
    }

    open func addSign(_ sign: String) {

        passcode.append(sign)
        delegate?.passcodeLock(self, addedSignAtIndex: passcode.count - 1)

        if passcode.count >= configuration.passcodeLength {

            lockState.acceptPasscode(passcode, fromLock: self)
            passcode.removeAll(keepingCapacity: true)
        }
    }

    open func removeSign() {

        guard passcode.count > 0 else { return }

        passcode.removeLast()
        delegate?.passcodeLock(self, removedSignAtIndex: passcode.count)
    }

    open func changeStateTo(_ state: PasscodeLockStateType) {

        lockState = state
        delegate?.passcodeLockDidChangeState(self)
    }

    open func authenticateWithBiometrics() {

        guard isTouchIDAllowed else { return }

        let context = LAContext()
        let reason = NSLocalizedString("PasscodeLockTouchIDReason", comment: "TouchID authentication reason")

        context.localizedFallbackTitle = localizedStringFor("PasscodeLockTouchIDButton", comment: "TouchID authentication fallback button")

        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
            success, error in

            self.handleTouchIDResult(success)
        }
    }

    fileprivate func handleTouchIDResult(_ success: Bool) {

        DispatchQueue.main.async {

            if success {

                self.delegate?.passcodeLockDidSucceed(self)
            }
        }
    }

    fileprivate func isTouchIDEnabled() -> Bool {

        let context = LAContext()

        return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
    }
}

1 个答案:

答案 0 :(得分:0)

您是否尝试过委托回VC或使用完成块,以便您的VC可以处理演示文稿?利用现有(viewController:animated :) API。

的最佳实践