I made a program to calculate the total width/height of views (sometimes I want the total width, sometimes I want the total height). The only catch is: If I'm calculating the width, I want to add an extra 10
to the total. Here's my current code:
func calculateLengthOfAllViews(calculatingWidth: Bool) {
let views = [
UIView(frame: CGRect.zero),
UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)),
UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
]
var totalLength: CGFloat = 0
if calculatingWidth {
totalLength += 10 /// add extra 10 if calculating width
} else {
totalLength += 0
}
for view in views { /// add each view's width/height
let length: CGFloat
if calculatingWidth {
length = view.frame.width
} else {
length = view.frame.height
}
totalLength += length
}
print("Total length is \(totalLength)")
}
calculateLengthOfAllViews(calculatingWidth: true) /// Total length is 160.0
calculateLengthOfAllViews(calculatingWidth: false) /// Total length is 100.0
This works fine. But, I'm repeating if calculatingWidth {
in 2 places, to determine:
view.frame.width
or view.frame.height
as the lengthThe second if-statement is unnecessary, because it always evaluates to the same thing in every iteration.
So, I think keypaths are the way to go -- I can store a reference to either .width
or .height
from the first if-statement. However, how do I define a keypath without "initializing" it? I would like to do something like this:
let keyPath: KeyPath /// Reference to generic type 'KeyPath' requires arguments in <...>
if calculatingWidth {
totalLength += 10
keyPath = \UIView.frame.width
} else {
totalLength += 0
keyPath = \UIView.frame.height
}
for view in views {
let length = view[keyPath: keyPath] /// Type of expression is ambiguous without more context
totalLength += length
}
However, this gives me Reference to generic type 'KeyPath' requires arguments in <...>
. How can I fix this?
class KeyPath<Root, Value>
is a generic type with two type parameters: the root type and the result value type, and those must be specified in the declaration of the variable. In your case it would be
let keyPath: KeyPath<UIView, CGFloat>
With that definition, the remaining code compiles as well.
You are using if
at the start of the function, and if
on every iteration of the loop. I think a better way to do this would be to use reduce(into:_:)
, to reduce the number of conditional branches.
Code:
func calculateLengthOfAllViews(calculatingWidth: Bool) {
let views = [
UIView(frame: CGRect.zero),
UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)),
UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
]
let totalLength: CGFloat
if calculatingWidth {
totalLength = views.reduce(10, { $0 + $1.frame.width })
} else {
totalLength = views.reduce(0, { $0 + $1.frame.height })
}
print("Total length is \(totalLength)")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With