DiscreteFormatStyle
A format style that transforms a continuous input into a discrete output and provides information about its discretization boundaries.
- iOS
- 16.4+
- macOS
- 13.3+
- tvOS
- 16.4+
- watchOS
- 9.4+
protocol DiscreteFormatStyle<FormatInput, FormatOutput> : FormatStyle
Browse conforming typesUse this protocol to keep displays up to date if input changes continuously, or to iterate over all possible outputs of a FormatStyle
by obtaining the next discrete input in either direction from discreteInput(before:)
or discreteInput(after:)
.
Ordering of Inputs
The ordering over FormatInput
defined by discreteInput(before:)
/ discreteInput(after:)
must be consistent between the two functions. If FormatInput
conforms to the Comparable
protocol, the format style’s ordering should be consistent with the canonical ordering defined via the Comparable
conformance, i.e. it should hold that discreteInput(before: x)! < x < discreteInput(after: x)!
where discrete inputs are not nil.
Stepping through Discrete Input/Output Pairs
One use case of this protocol is enumerating all discrete inputs of a format style and their respective outputs.
While the discreteInput(before:)
and discreteInput(after:)
functions are the right tool for that, they do not give a guarantee that their respective return values actually produce an output that is different from the output produced by formatting the input
value used when calling discreteInput(before:)
/ discreteInput(after:)
, they only provide a value that produces a different output for most inputs. E.g. when formatting a floating point value as an integer, we can get the next discrete input after x
by calculating floor(x + 1)
. However, when rounding toward zero, the whole interval (-1;1) formats as zero. It would be ok for a discrete format style to ignore that edge case and return 0
for the discreteInput(after:)
a negative value greater than -1
. Therefore, to enumerate all discrete input/output pairs, adjacent outputs must be deduplicated in order to guarantee no adjacent outputs are the same.
The following example produces all discrete input/output pairs for inputs in a given range
making sure adjacent outputs are unequal:
extension DiscreteFormatStyle
where FormatInput : Comparable, FormatOutput : Equatable
{
func enumerated(
in range: ClosedRange<FormatInput>
) -> [(input: FormatInput, output: FormatOutput)] {
var input = range.lowerBound
var output = format(input)
var pairs = [(input: FormatInput, output: FormatOutput)]()
pairs.append((input, output))
// get the next discretization bound
while let nextInput = discreteInput(after: input),
// check that it is still in the requested `range`
nextInput <= range.upperBound {
// get the respective formatted output
let nextOutput = format(nextInput)
// deduplicate based on the formatted output
if nextOutput != output {
pairs.append((nextInput, nextOutput))
}
input = nextInput
output = nextOutput
}
return pairs
}
}
Imperfect Discretization Boundaries
In some scenarios, a format style cannot provide precise discretization boundaries in a performant manner. In those cases it must override input(before:)
and input(after:)
to reflect that. For any discretization boundary x
returned by either discreteInput(before:)
or discreteInput(after:)
based on the original input y
, all values representable in the FormatInput
strictly between x
and the return value of input(after: x)
or input(before: x)
, respectively, are not guaranteed to produce the same formatted output as y
.
The following schematic shows an overview of the guarantees given by the protocol:
xB = discreteInput(before: y) y xA = discreteInput(after: y)
| | |
<-----+---+-------------------------+-------------------------+---+--->
| |
zB = input(after: xB) zA = input(before: xA)
the formatted output for everything in
zB...zA
(including bounds) is guaranteed to be equal toformat(y)
the formatted output for
xB
and lower is most likely different fromformat(y)
the formatted output for
xA
and higher is most likely different fromformat(y)
the formatted output between
xB
andzB
, as well aszA
andxA
(excluding bounds) cannot be predicted