Monday, June 6, 2016

Changing View When Showing Keyboard

 
var keyboardIsShowing = false

override func viewDidAppear(_ animated: Bool) {  
    NotificationCenter.default.addObserver(self, selector: 
        #selector(keyboardDidShow), 
        name: Notification.Name.UIKeyboardDidShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: 
        #selector(keyboardWillHide), 
        name: Notification.Name.UIKeyboardWillHide, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    NotificationCenter.default.removeObserver(self, 
        name: Notification.Name.UIKeyboardDidShow, object: self)
    NotificationCenter.default.removeObserver(self, 
        name: Notification.Name.UIKeyboardWillHide, object: self)
}
 

Not In A Scroll View

 
@objc 
func keyboardDidShow(notification: NSNotification) {
    
    if keyboardIsShowing { return }
    
    let keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
        
    keyBoardIsShowing = true
        
    var newFrame = self.view.frame
    newFrame.origin.y -= keyboardSize.height
    self.view.frame = newFrame
}

@objc 
func keyboardWillHide(notification: NSNotification) {
    let keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
        
    keyboardIsShowing = false
        
    var newFrame = self.view.frame
    newFrame.origin.y += keyboardSize.height
}
 
Note: While this works fine, I always had trouble getting it to show smoothly or even animate it. It is choppy. As of this date I recommend just putting your view in a scroll view and using the below methods for a smoother look and feel.

In A Scroll View

 
@IBOutlet weak var scrollView: UIScrollView!

@objc 
func keyboardDidShow(notification: NSNotification) {
    
    if keyboardIsShowing { return }
    
    let keyboardSize = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size
        
    keyboardIsShowing = true
    
    let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
    scrollView.contentInset = contentInsets
    scrollView.scrollIndicatorInsets = contentInsets

    // OPTIONAL: When you want bottom of a view to rest on the top of the keyboard.
    let viewBottom = myView.frame.origin.y + myView.frame.height
    let keyboardTop = view.bounds.height - keyboardSize.height

    if viewBottom > keyboardTop {
        let y = viewBottom - keyboardTop + 5
        scrollView.setContentOffset(CGPoint(x: 0, y: y), animated: true)
    }
}

@objc 
func keyboardWillHide(notification: NSNotification) {
    keyboardIsShowing = false

    let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    scrollView.contentInset = contentInsets
    scrollView.scrollIndicatorInsets = contentInsets
}
 

(Xcode 9, Swift 4)

Saturday, June 4, 2016

Modal Popups on iPhone

The way to achieve the look of a modal popup on the iPhone lies in the settings of the Storyboard Segue from the view controller that will be triggering the popup to the view controller that contains the popup.

I choose Cross Dissolve for the Transition so the popup doesn't slide into view but materializes into view on top instead.

You can do all this in just one view controller but I did it this way to reuse the popup in many view controllers and I like the simplicity of it.

For the modal popup view controller I will sometimes add a blur effect or a view with a lowered alpha property so you can still see the underlying screen a little and then put the content in a smaller size.
Here is an example:

View Hierarchy

If you don't get the right view hierarchy, the transition will be messed up.

Wrong Way

Right Way


Close the Popup

Finally I want the user to be able to touch anywhere off the popup to dismiss it. This can be done by adding a Tap Gesture Recognizer or a button with no text to cover the whole screen below the popup's view.

(Xcode 8)

Navigation

Here are some ways to programmatically control navigation.
In order to find your view controllers by identifier you will have to first set the identifier (after selecting it on the storyboard):

With NavigationController

 
// Navigate to another view controller
let vc = storyboard?.instantiateViewControllerWithIdentifier("myViewController")
navigationController?.pushViewController(vc!, animated: true)

// Go to previous view controller
dismissViewControllerAnimated(true, completion: nil)

// Go to the starting view controller
navigationController?.popToRootViewControllerAnimated(true)
 

Without NavigationController

 
// Navigate to another view controller
let vc = storyboard?.instantiateViewControllerWithIdentifier("myViewController")
present(vc!, animated: true, completion: nil)

// Go to previous view controller
dismissViewControllerAnimated(true, completion: nil)
 

With Segue

 
// Navigate to another view controller
// You have to give your Segue an identifier to use this
performSegue(withIdentifier: "mySegue", sender: self)
 

(Swift 2.2)

Friday, June 3, 2016

Setting Colors

Find the color that you want by opening the color picker in Xcode for any property where you can select a color.
(See previous post on how to find the color picker.)

After you have your color go to the "Color Sliders" tab of the color picker (second tab) to get the red, green and blue values.

Then you plug those numbers into the UIColor constructor and divide them by 255.
 
let color = UIColor(red: 106/255, green: 126/255, blue: 123/255, alpha: 1.0)
 

(Swift 2.2)

Color Picker in Xcode?

This blog is normally dedicated to Swift but I wanted to create a quick post for all my friends that use Xcode and didn't know about the built-in color picker and color saver.
(Not sure if it is really called a "color saver". I just made that up! :D)

Where is it?

You want to click on the left side of the color selector:

This will bring up the color selector:
From here you can click on the eye dropper tool, pick a color and then you can save that color by dragging the color from the big square to one of the smaller squares.

Note: The colors in the color saver/color manager persist across all projects.
(Xcode 7.3)

Thursday, June 2, 2016

How to Scale Objects

Words

transform

- You set a UIView's transform property to scale it. The view is transformed using mathematical formulas in an object that you supply. In this case, you are supplying a CGAffineTransformMakeScale object with two parameters.

Affine

- Means to preserve parallel relationships. When you transform a view you might be scaling it (the sides of the view are still parallel to each other). You may also be rotating the view in which the sides are still parallel to each other.

CGAffineTransformMakeScale

- This object will hold the values you supply and apply the correct transformation to scale the view. Takes two parameters:
  • Scale X (sx) - A number by which you want to scale the width.
  • Scale Y (sy) - A number by which you want to scale the height.
If you provide a 2 that means you want the view to be twice the width/height.
If you provide a 1 that just means you want the width/height to be the original size.
A number lower than 1 will shrink the size of the view.

Examples

 
class ViewController: UIViewController {
    
    @IBOutlet weak var greenView: UIView!
    
    @IBAction func biggerButton(sender: AnyObject) {
        // Scale the view to be twice as big.
        greenView.transform = CGAffineTransformMakeScale(2, 2)
    }
    
    @IBAction func resetButton(sender: AnyObject) {
        // Reset the view to be normal size.
        greenView.transform = CGAffineTransformMakeScale(1, 1)
    }
    
    @IBAction func halfSizeButton(sender: AnyObject) {
        // Scale the view to be half the original size.
        greenView.transform = CGAffineTransformMakeScale(0.5, 0.5)
    }
    
    @IBAction func TwiceTall(sender: AnyObject) {
        // Keep width 1x, make height 2x.
        greenView.transform = CGAffineTransformMakeScale(1, 2)
    }
    
    @IBAction func TwiceWide(sender: AnyObject) {
        // Make width 2x, make height 1x.
        greenView.transform = CGAffineTransformMakeScale(2, 1)
    }

    @IBAction func HideButton(sender: AnyObject) {
        // Scaling to zero hides the view.
        greenView.transform = CGAffineTransformMakeScale(0, 0)
    }
}
 

(Swift 2.2)

SwiftUI Search & Filter with Combine - Part 3 (iOS, Xcode 13, SwiftUI, 2...

In part 3 of the Searchable video series, I show you how to use Combine in #SwiftUI for the search and filter logic connected to the searcha...