消除Interactive View Controller问题

时间:2018-06-20 04:51:15

标签: ios swift4 uiviewanimation interactive

我在关闭动画时遇到问题,无法以交互方式关闭第二个ViewController。我已经为我的课程实现了UIPercentDrivenInteraction,但是当我解雇它时,它会执行常规的基于时间的动画。了解我在做什么错的任何帮助都会有所帮助。这是我目前的动画课内容:

class leftSwipeAnimator: UIPercentDrivenInteractiveTransition, 
UIViewControllerAnimatedTransitioning {

let time = 0.3
var presenting = true
var dismissCompletion: (()->Void)?
let screenSize = UIScreen.main.bounds
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return time
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    let containerView = transitionContext.containerView
    let toView = transitionContext.view(forKey: .to)!
    let secondView = presenting ? toView : transitionContext.view(forKey: .from)!

    let originPoint: CGPoint = CGPoint(x: screenWidth, y: 0)
    let originSize: CGSize = CGSize(width: screenWidth, height: screenHeight)
    let originFrame = CGRect(origin: originPoint, size: originSize)
    let initialFrame = presenting ? originFrame : secondView.frame
    let finalFrame = presenting ? secondView.frame : originFrame

    if presenting {
        secondView.center = CGPoint(
            x: initialFrame.midX,
            y: initialFrame.midY)
        secondView.clipsToBounds = true
    }

    containerView.addSubview(toView)
    containerView.bringSubview(toFront: secondView)

    UIView.animate(withDuration: time, delay:0.0,
                   animations: {
                    secondView.center = CGPoint(x: finalFrame.midX, y: finalFrame.midY)
    }, completion: { _ in
        if !self.presenting {
            self.dismissCompletion?()
        }
        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
    })
}

func handlePan( recognizer: UIPanGestureRecognizer){
    let translation = recognizer.translation(in:
        recognizer.view!.superview!)
    var progress: CGFloat = abs(translation.x / 300.0) * 0.5
    progress = min(max(progress, 0.01), 0.99)

    print(progress)

    switch recognizer.state {
    case .cancelled, .ended:
        if progress < 0.4 {
            cancel()
        } else {
            finish() }
    case .changed:
        update(progress)
    default:
        break
    }

  }
}

这是我在主ViewController中的实现:

import UIKit
import Security

class ViewController: UIViewController {
var userId = "testing2" // This will have to be something unique to each user in the future. Don't know how we'll implement this yet

let transition = leftSwipeAnimator()
let right = rightSwipeAnimator()
var sideCheck: Int = 0 // Right is 0, left is 1

@IBAction func leftPage(_ sender: UIButton) {
    sideCheck = 1
    let mainStoryBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
    let screen3: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "VC3") as! thirdViewController
    screen3.transitioningDelegate = self
    present(screen3, animated: true, completion: nil)
}

@IBAction func nextPage(_ sender: UIButton) {
    rightPage()
}

func rightPage() {
    sideCheck = 0
    let mainStoryBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
    let screen2: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "VC2") as! secondViewController
    screen2.transitioningDelegate = self
    present(screen2, animated: true, completion: nil)
}


@IBAction func deleteKey(_ sender: Any) { deleteKey() }
@IBOutlet weak var encryptString: UITextField!
@IBOutlet weak var encryptKey: UITextField!
@IBOutlet weak var encryptOutput: UITextField!
@IBOutlet weak var decryptString: UITextField!
@IBOutlet weak var decryptKey: UITextField!
@IBOutlet weak var decryptOutput: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()
    print(keyCheck(unique: userId))
    let recognizer = UIPanGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
    self.view.addGestureRecognizer(recognizer)

    // Do any additional setup after loading the view, typically from a nib.

    //generateKey() // We will only have to run this function once per every new user to save the private key in their device

}



override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

@IBAction func encrypt(_ sender: UIButton) {
    var item: CFTypeRef?
    var error: Error
    var enError: Unmanaged<CFError>?
    var key: SecKey
    let checkForKey = keyCheck(unique: userId)
    let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512  // This is where we set the algorithm. Decryption algorithm and encyrption algorithm must match.
    let stringToEn = encryptString.text!.data(using: .utf8)! // Take the input string and make it into a data object with UTF-8 formatting
    let cfdataToEn = stringToEn as CFData // Take the above Data input string and make it into a CFData

    // If we have no key in the device matching our tag, generate one
    if (checkForKey == false) {
        generateKey()
    }

    // Retrieve Private Key
    let tag = "com.One28.One28-user.\(userId)".data(using: .utf8)!

    let getQuery: [String: Any] = [kSecClass as String: kSecClassKey, // This is a query we make to find the Private key in the key chain based of the input we put inside of it
        kSecAttrApplicationTag as String: tag, // We use the tag to search for our saved Private key in the KeyChain as it is unique
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA, // This says the keychain will be of type RSA
        kSecReturnRef as String: true] // This says to return a reference to the key, and not the key data itself

    let status = SecItemCopyMatching(getQuery as CFDictionary, &item)

    if status == errSecSuccess {
        key = item as! SecKey
    }
    else {
        print("There was an error retrieving and setting the key in encryption.")
        return
    }

    let pubKey = SecKeyCopyPublicKey(key) // Generate our public key from the Private Key for our User.

    // This checks if the encryption type is supported by our public Key
    do {
        guard SecKeyIsAlgorithmSupported(pubKey!, .encrypt, .rsaEncryptionOAEPSHA512)

            else {
                error = "Error occurred when checking if encryption type is supported" as! Error // Errors like this will crach. Don't know how to fix it though.
                throw error
        }
    } catch{ print("Our public key does not support this algorithm encryption") }

    // This checks to make sure that our is smaller than 130 bytes smaller than our keys block size.
    // This is necessary because the size has to be less than the allocated bits in the Private Key
    do {
        guard ((encryptString.text?.count)! < (SecKeyGetBlockSize(pubKey!)-130))
            else {
                error = "Text is too long to encrypt" as! Error
                throw error
            }
    } catch { print("Input text is too large to encrypt.") }

     //This does the actual encryption
    do {
        guard let cipherText = SecKeyCreateEncryptedData(pubKey!,
                                                         algorithm,
                                                          cfdataToEn as CFData,
                                                         &enError) as Data? else {
                                                            throw enError!.takeRetainedValue() as Error
        }

        let stringKey: String = cipherText.base64EncodedString()
        encryptOutput.text = stringKey
    } catch { print(enError) }
}

@IBAction func decrypt(_ sender: UIButton) {
    var item: CFTypeRef?
    var error: Error
    var deError: Unmanaged<CFError>?
    var key: SecKey
    let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA512 // Decryption algorithm matching the encyrption algorithm
    let stringToDe: String = decryptString.text!        // Next 3 lines are similar to encryption method
    let dataToDe = Data(base64Encoded: stringToDe)
    let cfdataToDe = dataToDe! as CFData

    let tag = "com.One28.One28-user.\(userId)".data(using: .utf8)! // Here we see this important tag again

    let getQuery: [String: Any] = [kSecClass as String: kSecClassKey, // Similar to encyrption getQuery
        kSecAttrApplicationTag as String: tag,
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
        kSecReturnRef as String: true]

    let status = SecItemCopyMatching(getQuery as CFDictionary, &item)

    if status == errSecSuccess {
        key = item as! SecKey
    }
    else {
        print("there was an error")
        return
    }

    // This checks if the key supports our algorithm for decryption
    do {
        guard SecKeyIsAlgorithmSupported(key, .decrypt, algorithm)
            else {
                error = "Error occurred when checking if key supports decryption algorithm" as! Error
                throw error
            }
    } catch { print(error) }

    // This checks to see if our cipher text is the same as our keys blocksize
    let dataSize: Int = (dataToDe?.count)!
    do {
        guard (dataSize == SecKeyGetBlockSize(key))
            else {
                error = "Error occurred when checking cipher text count" as! Error
                throw error
            }
    } catch { print(error) }

    // This does the decryption
    do {
        guard let clearText = SecKeyCreateDecryptedData(key,
                                                        algorithm,
                                                        cfdataToDe as CFData,
                                                        &deError) as Data? else {
                                                            throw deError!.takeRetainedValue() as Error
        }

        let stringKey: String = String(decoding: clearText, as: UTF8.self)
        decryptOutput.text = stringKey

    } catch { print(deError) }
}

// This function serves as a way to check if a Private Key with, a given unique identifier placed in our tag, exists
func keyCheck(unique: String) -> Bool {
    var error: Unmanaged<CFError>?
    let tag = "com.One28.One28-user.\(unique)".data(using: .utf8)! // As we see, this tag is important for finding the key in the KeyChain

    let getquery: [String: Any] = [kSecClass as String: kSecClassKey, // This is a query we make to find the Private key in the key chain based of the input we put inside of it
                                   kSecAttrApplicationTag as String: tag, // We use the tag to search for our saved Private key in the KeyChain as it is unique
                                   kSecAttrKeyType as String: kSecAttrKeyTypeRSA, // This says the keychain will be of type RSA
                                   kSecReturnRef as String: true] // This says to return a reference to the key, and not the key data itself

    // Now that we have a query, we can use SecItemCopyMatching to find any keys that fit our descirption

    var item: CFTypeRef? // This is an empty reference to populate with our private key temporarily
    let status = SecItemCopyMatching(getquery as CFDictionary, &item) //This performs the search for our private key in the key chain
    if status == errSecSuccess {
        return true
    }
    else {
        print("There was an error checking to see if a key existed. Most likely because a key with the given tag does not exist.")
    }
    return false
}

// This function generates a Private key and stores that Private key in the devices keychain
func generateKey(){
    print("Generating a new key")
    var error: Unmanaged<CFError>? // To catch error when making private key
    let tag = "com.One28.One28-user.\(userId)".data(using: .utf8)! // This is to find the private key in the keychain later

    let attributes: [String: Any] =        // This is to create an asymmetric key pair
        [kSecAttrKeyType as String: kSecAttrKeyTypeRSA,  //This is the key types algorithm
         kSecAttrKeySizeInBits as String: 2048,         //Size in bits
         kSecPrivateKeyAttrs as String:             //To set up Private key to put in the keychain
            [kSecAttrIsPermanent as String: true,  //Making this true puts the private key in the keychain
             kSecAttrApplicationTag as String: tag]  // This is a unique value to find the key later. Which is why we put the users ID at the end of the tag because the users ID will always be unique.
    ]

    do {
        guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) // Creates random key like implied
            else {
                throw error!.takeRetainedValue() as Error
            }

        if let cfdata = SecKeyCopyExternalRepresentation(privateKey, &error) {
            let data: Data = cfdata as Data
            let stringKey: String = data.base64EncodedString() // This is to make the key human readable
            //print("Private key generated: \(stringKey)")      // Uncomment this line if you'd like to see the SecKey object
        }
        else {
            print(error)
        }
    } catch { print(error) }
}

func deleteKey() {
    var secret: AnyObject?
    // Retrieve Private Key
    let tag = "com.One28.One28-user.\(userId)".data(using: .utf8)!
    print(secret)

    let getQuery: [String: Any] = [kSecClass as String: kSecClassKey, // This is a query we make to find the Private key in the key chain based of the input we put inside of it
        kSecAttrApplicationTag as String: tag, // We use the tag to search for our saved Private key in the KeyChain as it is unique
        kSecAttrKeyType as String: kSecAttrKeyTypeRSA, // This says the keychain will be of type RSA
        kSecReturnRef as String: kCFBooleanTrue,// This says to return a reference to the key, and not the key data itself
    ]

    let status = SecItemCopyMatching(getQuery as CFDictionary, &secret)
    print(secret!)

    let delQuery: [String: Any] = [
        kSecMatchItemList as String: secret,// This says to return a reference to the key, and not the key data itself
    ]

    let delStatus = SecItemDelete(delQuery as CFDictionary)

    guard delStatus == errSecSuccess || delStatus == errSecItemNotFound else {
        print(delStatus)
        print("Error")
        return
    }

    print ("success")

    if( delStatus == errSecSuccess || delStatus == errSecItemNotFound) {
        print ("problem deleting the key chain")
    }

    else {
        print("key deleted")
    }
}

}

extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if (sideCheck == 0) {
        transition.presenting = true

        return transition
    }

    else {
        right.presenting = true
        return right
    }
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if (sideCheck == 0) {
        transition.presenting = false
        return transition
    }

    else {
        right.presenting = false
        return right
    }
}

func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    transition.presenting = true
    return transition
}

func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    transition.presenting = false
    return transition

}

@objc func swipeAction(swipe: UIPanGestureRecognizer) {
    let mainStoryBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
    let screen2: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "VC2") as! secondViewController
    screen2.transitioningDelegate = self
    switch swipe.state {
    case .began:
        present(screen2, animated: true, completion: nil)
    default:
        transition.handlePan(recognizer: swipe)
    }
}
}

这是我在第二个ViewController中的实现:

import UIKit

class secondViewController: UIViewController, UIViewControllerTransitioningDelegate {

let transition = leftSwipeAnimator()

@IBAction func back(_ sender: UIButton) {
    presentingViewController?.dismiss(animated: true, completion: nil)
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let recognizer = UIPanGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
    self.view.addGestureRecognizer(recognizer)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/
}

extension secondViewController{
@objc func swipeAction(swipe: UIPanGestureRecognizer) {
    let mainStoryBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
    let screen1: UIViewController = mainStoryBoard.instantiateViewController(withIdentifier: "VC1") as! ViewController
    screen1.transitioningDelegate = self
    switch swipe.state {
    case .began:
        dismiss(animated: true, completion: nil)
    default:
        transition.handlePan(recognizer: swipe)
    }
}
}

谢谢您的帮助!请注意,我已经尝试过开一门新课程,只是为了解雇而已,这没有用。最终有同样的问题。

0 个答案:

没有答案