Problem building Tip Calculator with Swift 4.2 (latest version)


#1

I am in Lesson 3 at the section on using Notification Center to move the keyboard up and down and re-draw the UI accordingly. Using both the View Controller code I recreated on my own and the View Controller code that is included in Lesson 3 download, I found that I get the error if I go to the project settings under “Build Settings” when the Swift Language Version is set to 4.2 (which is what I had been using). If I set it back to 4.0 there is no error.

In the Lesson 3 downloaded View Controller, the 3 lines of code under “// Listen for keyboard events”, lines 26, 27 and 28 all get the same errors. The first error is a “Fix” error which says that ‘UIKeyboardWillShow’ has been renamed to ‘UIRespnder.keyboardWillShowNotification’. If I select “Fix” (will not build otherwise), I get a second error "Type Notification.Name(aka NSNotification.Name) has no member ‘UIResponder’ ".

Again, this error is just with Swift 4.2 (the latest version with Xcode 10.1) and not with Swift 4.0. I can just continue the lesson using 4.0 but am wondering if there is just some easy name or reference change that needs to be made.
Thank you!
Ted


#2

The fix-it’s should update the code to Swift 4.2.

Apple changed these properties (again) in Swift 4.2 and the iOS 12 SDK.

I’ll be posting some new videos for the changes in the near future.

Both of these enum values have been renamed.

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

is now

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)

NSNotification.Name.UIKeyboardWillShow became: UIResponder.keyboardWillShowNotification

There are a few more that have changed using a similar format in the viewDidLoad() method.

Your viewDidLoad() and deinit should look something like:

override func viewDidLoad() {
    super.viewDidLoad()

    billTextField.delegate = self
    
    print("Hello")
    print("Hi Paul! \(18)")
    
    // Listen for keyboard events
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}

deinit {
    // Stop listening for keyboard hide/show events
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}

Then in the keyboardWillChange() method, you’ll need to change the keys.

    guard let keyboardRect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {

is now

    guard let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {

Where UIKeyboardFrameEndUserInfoKey morphed into UIResponder.keyboardFrameEndUserInfoKey

The method should look something like:

@objc func keyboardWillChange(notification: Notification) {

    guard let keyboardRect = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
        return
    }
    
    if notification.name == UIResponder.keyboardWillShowNotification ||
        notification.name == UIResponder.keyboardWillChangeFrameNotification {
        
        view.frame.origin.y = -keyboardRect.height
    } else {
        view.frame.origin.y = 0
    }
}