Could not find an overload for "+" that accepts the supplied arguments - ios

Look at this code:
var timepenalty = UInt8(0)
var currentTime = NSDate.timeIntervalSinceReferenceDate()
// Find the difference between current time and start time
var elapsedTime: NSTimeInterval = currentTime - startTime
let adjustedTime = UInt8(timepenalty + elapsedTime)
error-
"Could not find an overload for "+" that accepts the requested arguments.
"
This is for a game that adds time to the stopwatch-style timer, every time the player makes a mistake. The code works when I just use an integer instead of the elapsedTime variable as so:
let adjustedTime = UInt8(elapsedTime + 5)
but replacing 5 with a variable gives an error.
Here's the full code for the updateTime function:
func updateTime() {
var currentTime = NSDate.timeIntervalSinceReferenceDate()
// Find the difference between current time and start time
var elapsedTime: NSTimeInterval = currentTime - startTime
let adjustedTime = UInt8(timepenalty + elapsedTime)
// calculate the minutes in elapsed time
let minutes = UInt8(elapsedTime / 60.0)
elapsedTime -= (NSTimeInterval(minutes) * 60)
// calculate the seconds in elapsed time
seconds = UInt8(elapsedTime)
elapsedTime -= NSTimeInterval(seconds)
// seconds += timepenalty
// find out the fraction of millisends to be displayed
let fraction = UInt8(elapsedTime * 100)
// if seconds > 20 {
// exceedMsgLabel.text = "超过20秒了"
// }
// add the leading zero for minutes, seconds and millseconds and store them as string constants
let startMinutes = minutes > 9 ? String(minutes):"0" + String(minutes)
let startSeconds = seconds > 9 ? String(seconds):"0" + String(seconds)
let startFraction = fraction > 9 ? String(fraction):"0" + String(fraction)
displayTimeLabel.text = "\(startMinutes):\(startSeconds):\(startFraction)"
var penalty = String(timepenalty)
penaltylabel.text = "+ " + penalty
}

#David's code is good, but I'd strongly recommend that you make adjustedTime be an NSTimeInterval. It is a time interval, and that's what the type is for. Then all your casting issues go away.
The UInt8 type is reserved for cases where you explicitly need an 8-bit bit-pattern (like for networking protocols or binary file formats). It isn't intended for "small numbers." Moving between signed and unsigned numbers and different sized-numbers are common sources of bugs, and is intentionally made cumbersome.
If you do need to force a Double to be a whole number, just use Int rather than UInt8 in most cases. In most of these cases it looks like you really mean floor() rather than Int() anyway. You're just normalizing to an whole number.
That said, a more typical way to do your formatting is:
import Foundation
let totalSeconds: NSTimeInterval = 100.51
let frac = Int((totalSeconds - floor(totalSeconds)) * 100)
let seconds = Int(totalSeconds % 60)
let minutes = Int((totalSeconds / 60) % 60)
let result = String(format: "%02d:%02d:%02d", minutes, seconds, frac)

This line:
let adjustedTime = UInt8(timepenalty + elapsedTime)
is attempting to add a UInt8 (time penalty) and an NSTimeInterval (double, elapsedTime) which fails as there is no implicit type conversion in Swift. Change it to:
let adjustedTime = timepenalty + UInt8(elapsedTime)
Which converts the NSTimeInterval to a UInt8 before the addition.

UInt8 and NSTimeInterval are two different types. You need to make each operand the same type. (Or you could use operator overloading.)

Related

Swift -Get Remainder by itself to Nearest Tenth from Double

I have a video and I need to get the seconds and milliseconds separated and the milliseconds rounded down to the nearest tenth. If the video duration starts off at 4.333333333333333 secs I use floor(num * 10) / 10 to get it to 4.3. When I try to get the remainder by itself using .truncatingRemainder(dividingBy: 1) and/or modf(num).1 I keep getting 0.2999999999999998 instead of 0.3. Why is this happening?
let mixCompositionDuration = CMTimeGetSeconds(mixComposition.duration)
print("mixCompositionDuration: ", mixCompositionDuration) // 4.333333333333333
let dubl = Double(mixCompositionDuration)
print("dubl: ", dubl) // 4.333333333333333
let mixCompositionRoundDown = floor(dubl * 10) / 10
print("mixCompositionRoundDown: ", mixCompositionRoundDown) // 4.3
let remainder = mixCompositionRoundDown.truncatingRemainder(dividingBy: 1)
print("remainder: ", remainder) // 0.2999999999999998
let mod = modf(mixCompositionRoundDown)
print("mod: ", mod) // (4.0, 0.2999999999999998)
let modRemainder = mod.1
print("modRemainder: ", modRemainder) // 0.2999999999999998

Running sum in background

Modified Question.
My fitness app will calculate the number of calories burned based on a calculated value for each second. I have a timer that will allow the app to pick back up should it. I can't get the running sum to continue calculating when the app goes into the background. I tried to place the running sum inside of a DispatchQueue but not getting the sum as expected. Any guidance is greatly appreciated.
Here's the code I have placed inside the function that updates the timer.
//MARK: - Update Timer Label
func updateTimerLabel() {
interval = -Int(timerStartDate.timeIntervalSinceNow)
time = interval
let hours = interval / 3600
let minutes = interval / 60 % 60
let seconds = interval % 60
print("Current interval = \(interval)")
timerLabel.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
DispatchQueue.global(qos: .background).async {
if self.activityArray[self.currentArrayRow].2 <= 4.5 {
self.cps = self.activityArray[self.currentArrayRow].2 * Double(self.user.userWeightInKilo) / 3600
self.runningCPS = self.runningCPS + self.cps
print("MET \(self.activityArray[self.currentArrayRow].2) <= 4.5 * KG (\(Double(self.user.userWeightInKilo))) * HR (\(Double(self.user.userHeartRate))) / MaxHR (\(Double(self.user.maxHeartRate)) * interval \(Double(self.interval)) / 3600. Gives a cps 0f \(self.cps) and a runningCPS of \(self.runningCPS) ")
} else {
self.cps = self.activityArray[self.currentArrayRow].2 * Double(self.user.userWeightInKilo) * Double(self.user.userHeartRate) / Double(self.user.maxHeartRate) / 3600
self.runningCPS = self.runningCPS + self.cps
print("MET \(self.activityArray[self.currentArrayRow].2) > 4.5 * KG (\(Double(self.user.userWeightInKilo))) * HR (\(Double(self.user.userHeartRate))) / MaxHR (\(Double(self.user.maxHeartRate)) * interval \(Double(self.interval)) / 3600. Gives a cps 0f \(self.cps) and a runningCPS of \(self.runningCPS) ")
}
}
activeLabel.text = String(format: "%0.1f", runningCPS) + " Calories Burned"
}

Swift - Hours/Minutes/Seconds to Integer conversion

I have Hours / Minutes / Seconds that I would like converted into an integer
7 Hours 30 Minutes 5 Seconds
You could use split():
import Foundation
func getSecondsFromString(timeString: String) -> (Int) {
let timeParts = timeString.replacingOccurrences(of: "[^0-9]", with: " ", options: [.regularExpression])
.split(separator: " ")
.map{Int($0)!}
return timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]
}
print(getSecondsFromString(timeString: "7 Hours 30 Minutes 5 Seconds"))
Output:
27005
If you have the time difference between two dates in seconds using timeIntervalSince then you can directly convert this into hours as a double by doing
let hours: Double = elapsedTime / 3600 // 7.501388...
In your examples this is 7.5 which you can then multiply with the hourly rate. If you for some reason only want to use full hours you can either round to the nearest full hour
let fullHours = round(hours) // 8.0
or if you want to truncate minutes and keep the hour then you can do a integer division from the start
let hours: Int = elapsedTime / 3600 // 7

What does "% is unavailable: Use truncatingRemainder instead" mean?

I get the following error when using code for an extension, I'm not sure if they're asking to just use a different operator or modify the values in the expression based on an internet search.
Error: % is unavailable: Use truncatingRemainder instead
Extension code:
extension CMTime {
var durationText:String {
let totalSeconds = CMTimeGetSeconds(self)
let hours:Int = Int(totalSeconds / 3600)
let minutes:Int = Int(totalSeconds % 3600 / 60)
let seconds:Int = Int(totalSeconds % 60)
if hours > 0 {
return String(format: "%i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}
}
The error(s) occur when setting the minutes and seconds variables.
CMTimeGetSeconds() returns a floating point number (Float64 aka
Double). In Swift 2 you could compute the
remainder of a floating point division as
let rem = 2.5 % 1.1
print(rem) // 0.3
In Swift 3 this is done with
let rem = 2.5.truncatingRemainder(dividingBy: 1.1)
print(rem) // 0.3
Applied to your code:
let totalSeconds = CMTimeGetSeconds(self)
let hours = Int(totalSeconds / 3600)
let minutes = Int((totalSeconds.truncatingRemainder(dividingBy: 3600)) / 60)
let seconds = Int(totalSeconds.truncatingRemainder(dividingBy: 60))
However, in this particular case it is easier to convert the duration
to an integer in the first place:
let totalSeconds = Int(CMTimeGetSeconds(self)) // Truncate to integer
// Or:
let totalSeconds = lrint(CMTimeGetSeconds(self)) // Round to nearest integer
Then the next lines simplify to
let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
The % modulus operator is defined only for integer types. For floating-point types, you need to be more specific about the kind of IEEE 754 division/remainder behavior you want, so you have to call a method: either remainder or truncatingRemainder. (If you're doing floating-point math you actually need to care about this, and lots of other stuff, or you can get unexpected / bad results.)
If you actually intend to do integer modulus, you need to convert the return value of CMTimeGetSeconds to an integer before using %. (Note that if you do, you'll lop off the fractional seconds... depending on where you're using CMTime that may be important. Do you want minutes:seconds:frames, for example?)
Depending on how you want to present CMTime values in your UI, it might be better to extract the seconds value and pass it to NSDateFormatter or NSDateComponentsFormatter so you get appropriate locale support.
Bring back the simple modulo syntax in swift 3:
This syntax was actually suggested on Apples official swift mailing list here but for some reason they opted for a less elegant syntax.
infix operator %%/*<--infix operator is required for custom infix char combos*/
/**
* Brings back simple modulo syntax (was removed in swift 3)
* Calculates the remainder of expression1 divided by expression2
* The sign of the modulo result matches the sign of the dividend (the first number). For example, -4 % 3 and -4 % -3 both evaluate to -1
* EXAMPLE:
* print(12 %% 5) // 2
* print(4.3 %% 2.1) // 0.0999999999999996
* print(4 %% 4) // 0
* NOTE: The first print returns 2, rather than 12/5 or 2.4, because the modulo (%) operator returns only the remainder. The second trace returns 0.0999999999999996 instead of the expected 0.1 because of the limitations of floating-point accuracy in binary computing.
* NOTE: Int's can still use single %
* NOTE: there is also .remainder which supports returning negatives as oppose to truncatingRemainder (aka the old %) which returns only positive.
*/
public func %% (left:CGFloat, right:CGFloat) -> CGFloat {
return left.truncatingRemainder(dividingBy: right)
}
This simple swift 3 migration tip is part of a more comprehensive swift 3 migration guide with many insights (35k loc / 8-days of migration) http://eon.codes/blog/2017/01/12/swift-3-migration/
There's no need to create a separate modulo operator for floating point numbers, unless you think it makes the code safer. You can overload the % operator to accept floating point numbers like so:
func %<N: BinaryFloatingPoint>(lhs: N, rhs: N) -> N {
lhs.truncatingRemainder(dividingBy: rhs)
}
Usage
let a: Float80 = 10
let b: Float80 = 3
print(a % b)
You can now use % with any two floating point numbers of the same tye.
I found that the following works in Swift 3:
let minutes = Int(floor(totalSeconds / 60))
let seconds = Int(totalSeconds) % 60
where totalSeconds is a TimeInterval (Double).

How to customize my timer display to show only minutes and seconds?

My Timer is displaying Minutes and Hours, but once it gets to 60 minutes it restarts from 0 Minute.
Should I get rid of the modulo ( % 60 ) for minutes.
I would like my timer to display for ex: 80:45 ( basically not stopping at 60 min once it reaches 1 hour)
var min = 0
var sec = 0
func stringFromTimeInterval(interval: NSTimeInterval) -> String {
let interval = Int(interval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
//let hours = (interval / 3600)// I don't need the hours
return String(format: "%02d:%02d",minutes, seconds)
}
% 60 means that it will spit out a minutes value that is the remainder when divided by 60(minutes). This is most probably because for time in the form hh:mm, you want it to go from 5:59 to 6:00, not 5:60. So changing the following line will give you what you seek.
let minutes = (interval / 60) % 60 -> let minutes = interval / 60

Resources