CGRect imageBounds = self.view.bounds;
imageBounds.size.height -= self.footer.bounds.size.height;
self.imageView.bounds = imageBounds;
Even though we have auto-layout, I often find myself doing this kind of arithmetic with points, size or rects. In Objective-C, it required either generating dummy variables so you can modify members (as above), or really messy struct initialization syntax:
self.imageView.bounds = (CGRect) {
.origin = self.view.bounds.origin,
.size = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height -
self.footer.bounds.size.height) };
Fortunately, none of this boilerplate is necessary with Swift. Since Swift lets you extend even C structures with new methods, I wrote a handful of combinators that eliminate this kind of code. The above snippet can now be replaced with:
self.imageView.bounds = self.view.bounds.mapHeight { $0 - self.footer.size.height }
I can easily enlarge a scroll view's content size to hold its pages:
self.scrollView.contentSize = self.scrollView.bounds.size.mapWidth { $0 * CGFloat(pages.count) }
I can do calculations that previously would've required dozens of lines of code in just one or two:
let topHalfFrame = self.view.bounds.mapHeight { $0 / 2 }
let bottomHalfFrame = topHalfFrame.mapY { $0 + topHalfFrame.size.height }
These two lines will give me two frames that each take up half of the height of their parent view.
In cases where I simply need to set a value, I use the primitive "with..." functions:
self.view.bounds.withX(0).withY(0).withSize(0).withHeight(0)
Note that these methods can all be chained to create complex expressions.
The code for these methods is trivial, yet they give you a huge boost in expressive power.
GitHub project: https://github.com/moreindirection/SwiftGeometry
GitHub project: https://github.com/moreindirection/SwiftGeometry
Code
extension CGPoint {
func mapX(f: (CGFloat -> CGFloat)) -> CGPoint {
return self.withX(f(self.x))
}
func mapY(f: (CGFloat -> CGFloat)) -> CGPoint {
return self.withY(f(self.y))
}
func withX(x: CGFloat) -> CGPoint {
return CGPoint(x: x, y: self.y)
}
func withY(y: CGFloat) -> CGPoint {
return CGPoint(x: self.x, y: y)
}
}
extension CGSize {
func mapWidth(f: (CGFloat -> CGFloat)) -> CGSize {
return self.withWidth(f(self.width))
}
func mapHeight(f: (CGFloat -> CGFloat)) -> CGSize {
return self.withHeight(f(self.height))
}
func withWidth(width: CGFloat) -> CGSize {
return CGSize(width: width, height: self.height)
}
func withHeight(height: CGFloat) -> CGSize {
return CGSize(width: self.width, height: height)
}
}
extension CGRect {
func mapX(f: (CGFloat -> CGFloat)) -> CGRect {
return self.withX(f(self.origin.x))
}
func mapY(f: (CGFloat -> CGFloat)) -> CGRect {
return self.withY(f(self.origin.y))
}
func mapWidth(f: (CGFloat -> CGFloat)) -> CGRect {
return self.withWidth(f(self.size.width))
}
func mapHeight(f: (CGFloat -> CGFloat)) -> CGRect {
return self.withHeight(f(self.size.height))
}
func withX(x: CGFloat) -> CGRect {
return CGRect(origin: self.origin.withX(x), size: self.size)
}
func withY(y: CGFloat) -> CGRect {
return CGRect(origin: self.origin.withY(y), size: self.size)
}
func withWidth(width: CGFloat) -> CGRect {
return CGRect(origin: self.origin, size: self.size.withWidth(width))
}
func withHeight(height: CGFloat) -> CGRect {
return CGRect(origin: self.origin, size: self.size.withHeight(height))
}
}
No comments:
Post a Comment