Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.

See all packages published by reuschj.

reuschj/Percent 1.0.1

Holds a percentage as a value.

⭐️ 1

🕓 1 week ago

.package(url: "https://github.com/reuschj/Percent.git", from: "1.0.1")

The `Percent`

type stores a value representing a percent (over 100). Normally, we would need to use decimals for calculations involving percents. However, decimals could be confused for absolute values. Let's look at this example:

```
let container: Double = 40
let widthScale: Double = 0.5
let width: Double = container * widthScale // 20
```

In the example above, we may not set an absolute value for something that should be a certain percent of some other value, so we could represent this with a decimal, but it's unclear if 0.5 means the absolute value is 0.5 or 50%.

Enter, `Percent`

, which makes the intention more clear:

```
let container: Double = 40
let widthScale = Percent(50)
let width: Double = container * widthScale // 20
```

So, we can use it the same way, but now the intention is very clear.

`Percent`

is initiated in any one of several different ways:

```
// By entering the percent (the number that gives you the decimal value when divided by 100)
let widthScale01 = Percent(50) // 50%
// By entering the decimal value
let widthScale02 = Percent(decimal: 0.5) // 50%
// By entering a string (Note that this produce an Optional, since you could enter an invalid value that will fail)
let widthScale03 = Percent("50%") // Optional(50%)
let widthScale04 = Percent("50") // Optional(50%) <-- This works too
let widthScale05 = Percent("fifty percent") // nil <-- Sorry, this doesn't work
// By entering a numerator and denominator
let widthScale06 = Percent(1, over: 2) // 50%
// By entering a one over denominator
let widthScale07 = Percent(oneOver: 2) // 50%
```

A `Percent`

is `CustomStringConvertible`

so will represent itself as a string in the form of "50%" or "25%".

You may notice the types `PercentDouble`

and `DecimalDouble`

. Both are just type aliases for `Double`

and can be used interchangeably with `Double`

. `PercentDouble`

should indicate that the number should be a percent (over 100) and `DecimalDouble`

should indicate that the number should be a decimal (over 1). For example, 50% is 50 as a `PercentDouble`

, but 0.5 as a `DecimalDouble`

.

You can also set an optional minimum and/or maximum value to constrain to. These are purely optional, but may be useful if receiving the percent as input and want to set an allowable range. For example:

```
// Assume `input` is a `DecimalDouble` we are getting from input. It could be anything, and may be out of our acceptable bounds.
// Let's constrain the input to be within 0% to 100%... if under, it will snap to the minimum. If over, it will snap to the maximum.
let percent01 = Percent(decimal: input, minimum: 0, maximum: 100)
// In this case, we'll only set a minimum, but won't set any upper bounds.
let percent02 = Percent(decimal: input, minimum: 0)
// In this case, we'll only set a maximum, but won't set any lower bounds.
let percent03 = Percent(decimal: input, maximum: 100)
// Omitting both minimum and maximum will allow any value we get from input
let percent04 = Percent(decimal: input)
```

A `Percent`

is `Equatable`

and `Comparable`

and be compared with any other `Percent`

.

```
let percent01 = Percent(50)
let percent02 = Percent(50)
let percent03 = Percent(25)
print(percent01 == percent02) // true
print(percent01 == percent03) // false
print(percent01 >= percent02) // true
print(percent01 > percent03) // true
```

A `Percent`

can be compared to a `DecimalDouble`

:

```
let percent01 = Percent(50)
print(percent01 >= 0.5) // true
print(percent01 > 0.3) // false
print(percent01 < 0.75) // true
```

Note that this will not work:

```
print(percent01 == 0.5) // false
```

This is because they effectively have the same value, but aren't the same type. So, this can't be checked with the `==`

operator, since that would imply them to be the same value *and* the same type. Instead, if you need to verify that a `Percent`

has the same value as a decimal, while not implying complete equality, you can use this method:

```
print(percent01.isEquivalentTo(decimal: 0.5)) // true
```

A `Percent`

can be combined with another `Percent`

.

```
print(Percent(50) + Percent(5)) // 55%
print(Percent(50) - Percent(5)) // 45%
print(Percent(50) * Percent(50)) // 25%
print(Percent(50) / Percent(25)) // 2
```

A `Percent`

can also be combined with a `Double`

.

```
print(10 + Percent(50)) // 15 <-- 10 + (10 * 0.5)
print(10 - Percent(50)) // 5 <-- 10 - (10 * 0.5)
print(10 * Percent(50)) // 5 <-- 10 * 0.5
print(10 / Percent(50)) // 20 <-- 10 / 0.5
print(Percent(25) * 2) // 50%
print(Percent(50) / 2) // 25%
```

For use in UI, use a `UIPercent`

. It works like a `Percent`

, with a few extras for UI. `Percent`

and `UIPercent`

both implement the `PercentProtocol`

protocol, so have many of the same capabilities. However, where `Percent`

is based on the `Double`

type and good for general arithmetic, `UIPercent`

is based on `CGFloat`

, so is better aligned to dealing with UI measurements.

Also, `UIPercent`

adds in the ability to set a scaling container of type `ScaleContainer`

. `ScaleContainer`

is an enum specifying a container that the percent intends to scale to. For example:

```
GeometryReader { geometry in
let width = UIPercent(25, of: .screen(.width))
let height = UIPercent(50, of: .screen(.height))
let screenSize: CGSize = geometry.size
let resolvedWidth = width.resolve(within: screenSize)
let resolvedHeight = height.resolve(within: screenSize)
}
```

The `ScaleContainer`

enum has two cases, `.screen`

and `.container`

. `.screen`

communicates that you are intending to scale to the full screen and `.container`

communicates that you intend to scale to some other container within the UI. Either way, you must specify the dimension of this container to scale to. `ScaleContainer.Dimension`

is an enum with cases `.height`

, `.width`

, `.radius`

, `.diameter`

and `.other`

(anything not covered by the other cases).

If you scale to `.container`

, you can also pass a string description. For example:

```
let handLength = UIPercent(90, of: .container(.radius, "clock"))
```

You can use the `resolve(within: CGFloat, limitedTo: ClosedRange<CGFloat>?)`

, `resolve(within: CGSize, limitedTo: ClosedRange<CGFloat>?)`

or `resolve(within: GeometryProxy, limitedTo: ClosedRange<CGFloat>?)`

method of `UIPercent`

to get the fixed value within the specified container. The second parameter is optional if you want to constrain that resolved value to an allowable range.

link |

Stars: 1 |

Last commit: 1 week ago |

WatchOS Support

1 week ago

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics