iOS Apprentice, 6th edition
Page 709 Exercise. My Solution Attempt
Exercise. See if you can rewrite the above logic to use a didSet property observer on the image instance variable. If you succeed, then placing the photo into image will automatically update the UIImageView, without needing to call show(image:).
I removed the show(_:)
method declaration and moved it’s definition into a property observer on image
. I then removed the call to show(_:)
within imagePickerController(_:didFinishPickingMediaWithInfo:)
var image: UIImage? {
didSet {
/// Exercise 708 Setting photo automatically updates UIImageView
imageView.image = image
imageView.isHidden = false
imageView.frame = CGRect(x: 10, y: 10,
width: 260, height: 260)
addPhotoLabel.isHidden = true
}
}
/// Tells the delegate that the user picked a still image or movie.
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
image = info[UIImagePickerControllerEditedImage] as? UIImage
// if let theImage = image {
// show(image: theImage)
// }
dismiss(animated: true, completion: nil)
}
This modification to image
affected future code on Page 725 where show(image: theImage) was called.
Chapter 30: Image Picker :: Edit the image :: Page 725
override func viewDidLoad() {
super.viewDidLoad()
if let location = locationToEdit {
title = "Edit Location"
if location.hasPhoto {
if let theImage = location.photoImage {
image = theImage // <-- changed code
}
}
}
}
Page 712 Exercise. My Solution Attempt
Exercise. Make the height of the photo table view cell dynamic, depending on the aspect ratio of the image. This is a tough one! You can keep the width of the image view at 260 points. This should correspond to the width of the UIImage object. You get the aspect ratio by doing image.size.width / image.size.height. With this ratio you can calculate what the height of the image view and the cell should be.
I divided 260 (width) by the aspect ratio to get the value of what the new image height should be. Used that height to create the CGRect
object that will be the new specs for imageView.frame
var image: UIImage? {
didSet {
/// Exercise 712 Dynamic table view cell height from image aspect ratio.
imageView.image = image
imageView.isHidden = false
// Height computed var
var height: Double {
let width = imageView.image!.size.width
let height = imageView.image!.size.height
let ratio = Double(width / height)
return 260 / ratio
}
imageView.frame = CGRect(x: 10, y: 10,
width: 260, height: height) // dynamic height
addPhotoLabel.isHidden = true
}
}
The matching case pattern where section == 1 returns the set imageView.frame.height
and I added 20 points of bottom padding, to match the top padding present at build time - as long as imageView.isHidden == false.
Refactored code pg 712-713
/// Asks the delegate for the height to use for a row in a specified location.
override func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
switch (indexPath.section, indexPath.row) {
case (0, 0):
return 88
case (1, _):
return imageView.isHidden ? 44 :
imageView.frame.height + 20 // dynamic height + 20 padding
case (2, 2):
addressLabel.frame.size = CGSize(
width: view.bounds.size.width - 115,
height: 10000)
addressLabel.sizeToFit()
addressLabel.frame.origin.x = view.bounds.size.width -
addressLabel.frame.size.width - 15
return addressLabel.frame.size.height + 20
default:
return 44
}
}
Page 731 Exercise. My Solution Attempt
Exercise. Change the resizing function in the UIImage extension to resize using the “Aspect Fill” rules instead of the “Aspect Fit” rules. Both keep the aspect ratio intact but Aspect Fit keeps the entire image visible while Aspect Fill fills up the entire rectangle and may cut off parts of the sides. In other words, Aspect Fit scales to the longest side but Aspect Fill scales to the shortest side.
I made it so when aspectFit is set to true, Aspect Fit rules are applied
When aspectFit is set to false, Aspect Fill rules are applied - Getting the bigger ratio, scales to the shortest side - and clips off the longer side exceeding the bounds.
I checkmarked the property clipsToBounds so that the clipping occurred. Content mode remained as Center.
In UIImage+Resize.swift
func resized(withBounds bounds: CGSize) -> UIImage {
// Calculates how big the image should be
// in order to fit inside the bounds rectangle.
let horizontalRatio = bounds.width / size.width
let verticalRatio = bounds.height / size.height
/// Exercise 731 Resize using Aspect Fill rules.
let aspectFit = false
let ratio = aspectFit ?
min(horizontalRatio, verticalRatio) : max(horizontalRatio, verticalRatio)
let newSize = CGSize(width: size.width * ratio,
height: size.height * ratio)
// Creates a new image context and draws the image into that.
...
}
NOTE In order to clip the excess length exceeding the set bounds of 52, I had to checkmark the UIImageView’s property clipsToBounds
Screen Shots
Cheers!
Rebecca