@@ -28,7 +28,7 @@ extension ByteCountFormatter {
28
28
// Can use any unit in showing the number.
29
29
public static let useAll = Units ( rawValue: 0x0FFFF )
30
30
}
31
-
31
+
32
32
public enum CountStyle : Int {
33
33
34
34
// Specifies display of file or storage byte counts. The actual behavior for this is platform-specific; on OS X 10.8, this uses the decimal style, but that may change over time.
@@ -47,7 +47,7 @@ open class ByteCountFormatter : Formatter {
47
47
}
48
48
49
49
public required init ? ( coder: NSCoder ) {
50
- super . init ( coder : coder )
50
+ NSUnimplemented ( )
51
51
}
52
52
53
53
/* Specify the units that can be used in the output. If NSByteCountFormatterUseDefault, uses platform-appropriate settings; otherwise will only use the specified units. This is the default value. Note that ZB and YB cannot be covered by the range of possible values, but you can still choose to use these units to get fractional display ("0.0035 ZB" for instance).
@@ -82,39 +82,31 @@ open class ByteCountFormatter : Formatter {
82
82
/* Specify the formatting context for the formatted string. Default is NSFormattingContextUnknown.
83
83
*/
84
84
open var formattingContext : Context = . unknown
85
-
85
+
86
86
/* A variable to store the actual bytes passed into the methods. This value is used if the includesActualByteCount property is set.
87
- */
87
+ */
88
88
private var actualBytes : String = " "
89
89
90
90
/* Create an instance of NumberFormatter for use in various methods
91
- */
91
+ */
92
92
private let numberFormatter = NumberFormatter ( )
93
93
94
94
/* Shortcut for converting a byte count into a string without creating an NSByteCountFormatter and an NSNumber. If you need to specify options other than countStyle, create an instance of NSByteCountFormatter first.
95
- */
95
+ */
96
96
open class func string( fromByteCount byteCount: Int64 , countStyle: ByteCountFormatter . CountStyle ) -> String {
97
97
let formatter = ByteCountFormatter ( )
98
98
formatter. countStyle = countStyle
99
99
return formatter. string ( fromByteCount: byteCount)
100
100
}
101
101
102
102
/* Convenience method on string(for:):. Convert a byte count into a string without creating an NSNumber.
103
- */
103
+ */
104
104
open func string( fromByteCount byteCount: Int64 ) -> String {
105
105
//Convert actual bytes to a formatted string for use later
106
- if includesActualByteCount {
107
- numberFormatter. numberStyle = . decimal
108
- actualBytes = numberFormatter. string ( from: NSNumber ( value: byteCount) ) !
109
- }
110
-
111
- if allowedUnits != . useDefault && allowedUnits != . useAll {
112
- if countStyle == . file || countStyle == . decimal {
113
- return unitsToUseFor ( byteCount: byteCount, byteSize: decimalByteSize)
114
- } else {
115
- return unitsToUseFor ( byteCount: byteCount, byteSize: binaryByteSize)
116
- }
117
- } else if countStyle == . decimal || countStyle == . file {
106
+ numberFormatter. numberStyle = . decimal
107
+ actualBytes = numberFormatter. string ( from: NSNumber ( value: byteCount) ) !
108
+
109
+ if countStyle == . decimal || countStyle == . file {
118
110
return convertValue ( fromByteCount: byteCount, for: decimalByteSize)
119
111
} else {
120
112
return convertValue ( fromByteCount: byteCount, for: binaryByteSize)
@@ -131,61 +123,143 @@ open class ByteCountFormatter : Formatter {
131
123
return string ( fromByteCount: Int64 ( value) )
132
124
}
133
125
134
- /* If allowedUnits has been set this function will ensure the correct unit is used and conversion is done. The conversion is done by making use of the divide method.
135
- */
136
- private func unitsToUseFor( byteCount: Int64 , byteSize: [ Unit : Double ] ) -> String {
137
- let bytes = Double ( byteCount)
138
-
139
- if bytes == 0 {
140
- return " Zero \( Unit . KB) "
141
- } else if bytes == 1 {
142
- return formatNumberFor ( bytes: bytes, unit: Unit . byte)
143
- }
144
-
145
- switch allowedUnits {
146
- case Units . useBytes: return formatNumberFor ( bytes: bytes, unit: Unit . bytes)
147
- case Units . useKB: return divide ( bytes, by: byteSize, for: . KB)
148
- case Units . useMB: return divide ( bytes, by: byteSize, for: . MB)
149
- case Units . useGB: return divide ( bytes, by: byteSize, for: . GB)
150
- case Units . useTB: return divide ( bytes, by: byteSize, for: . TB)
151
- case Units . usePB: return divide ( bytes, by: byteSize, for: . PB)
152
- case Units . useEB: return divide ( bytes, by: byteSize, for: . EB)
153
- case Units . useZB: return divide ( bytes, by: byteSize, for: . ZB)
154
- default : return divide ( bytes, by: byteSize, for: . YB)
155
-
156
- }
157
- }
158
-
159
126
/* This method accepts a byteCount and a byteSize value. Checks to see what range the byteCount falls into and then converts to the units determined by that range. The range to be used is decided by the byteSize parameter. The conversion is done by making use of the divide method.
160
- */
127
+ */
161
128
private func convertValue( fromByteCount byteCount: Int64 , for byteSize: [ Unit : Double ] ) -> String {
162
129
let byte = Double ( byteCount)
163
- if byte == 0 && allowsNonnumericFormatting {
164
- return " Zero \( Unit . KB) "
165
- } else if byte == 1 {
166
- return " \( byteCount) \( Unit . byte) "
167
-
130
+ if byte == 0 , allowsNonnumericFormatting, allowedUnits == . useDefault, includesUnit, includesCount {
131
+ return partsToIncludeFor ( value: " Zero " , unit: Unit . KB)
132
+ } else if byte == 1 || byte == - 1 {
133
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
134
+ return formatNumberFor ( bytes: byte, unit: Unit . byte)
135
+ } else {
136
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
137
+ }
168
138
} else if byte < byteSize [ Unit . KB] ! && byte > - byteSize[ Unit . KB] !{
169
- return formatNumberFor ( bytes: byte, unit: Unit . bytes)
170
-
139
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
140
+ return formatNumberFor ( bytes: byte, unit: Unit . bytes)
141
+ } else {
142
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
143
+ }
171
144
} else if byte < byteSize [ Unit . MB] ! && byte > - byteSize[ Unit . MB] ! {
172
- return divide ( byte, by: byteSize, for: . KB)
145
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
146
+ return divide ( byte, by: byteSize, for: . KB)
147
+ }
148
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
173
149
174
150
} else if byte < byteSize [ Unit . GB] ! && byte > - byteSize[ Unit . GB] ! {
175
- return divide ( byte, by: byteSize, for: . MB)
151
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
152
+ return divide ( byte, by: byteSize, for: . MB)
153
+ }
154
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
176
155
177
156
} else if byte < byteSize [ Unit . TB] ! && byte > - byteSize[ Unit . TB] ! {
178
- return divide ( byte, by: byteSize, for: . GB)
157
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
158
+ return divide ( byte, by: byteSize, for: . GB)
159
+ }
160
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
179
161
180
162
} else if byte < byteSize [ Unit . PB] ! && byte > - byteSize[ Unit . PB] ! {
181
- return divide ( byte, by: byteSize, for: . TB)
163
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
164
+ return divide ( byte, by: byteSize, for: . TB)
165
+ }
166
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
182
167
183
168
} else if byte < byteSize [ Unit . EB] ! && byte > - byteSize[ Unit . EB] ! {
184
- return divide ( byte, by: byteSize, for: . PB)
169
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
170
+ return divide ( byte, by: byteSize, for: . PB)
171
+ }
172
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
185
173
186
174
} else {
187
- return divide ( byte, by: byteSize, for: . EB)
175
+ if allowedUnits. contains ( . useAll) || allowedUnits == . useDefault {
176
+ return divide ( byte, by: byteSize, for: . EB)
177
+ }
178
+ return valueToUseFor ( byteCount: byte, unit: allowedUnits)
179
+ }
180
+ }
181
+
182
+ /*
183
+ A helper method to deal with the Option Set, caters for setting an individual value or passing in an array of values.
184
+ Returns the correct value based on the units that are allowed for use.
185
+ */
186
+ private func valueToUseFor( byteCount: Double , unit: ByteCountFormatter . Units ) -> String {
187
+ var byteSize : [ Unit : Double ]
188
+
189
+ //Check to see whether we're using 1000bytes per KB or 1024 per KB
190
+ if countStyle == . decimal || countStyle == . file {
191
+ byteSize = decimalByteSize
192
+ } else {
193
+ byteSize = binaryByteSize
194
+ }
195
+ if byteCount == 0 , allowsNonnumericFormatting, includesCount, includesUnit {
196
+ return partsToIncludeFor ( value: " Zero " , unit: Unit . KB)
188
197
}
198
+ //Handles the cases where allowedUnits is set to a specific individual value. e.g. allowedUnits = .useTB
199
+ switch allowedUnits {
200
+ case Units . useBytes: return partsToIncludeFor ( value: actualBytes, unit: Unit . bytes)
201
+ case Units . useKB: return divide ( byteCount, by: byteSize, for: . KB)
202
+ case Units . useMB: return divide ( byteCount, by: byteSize, for: . MB)
203
+ case Units . useGB: return divide ( byteCount, by: byteSize, for: . GB)
204
+ case Units . useTB: return divide ( byteCount, by: byteSize, for: . TB)
205
+ case Units . usePB: return divide ( byteCount, by: byteSize, for: . PB)
206
+ case Units . useEB: return divide ( byteCount, by: byteSize, for: . EB)
207
+ case Units . useZB: return divide ( byteCount, by: byteSize, for: . ZB)
208
+ case Units . useYBOrHigher: return divide ( byteCount, by: byteSize, for: . YB)
209
+ default : break
210
+ }
211
+
212
+ //Initialise an array that will hold all the units we can use
213
+ var unitsToUse : [ Unit ] = [ ]
214
+
215
+ //Based on what units have been selected for use, build an array out of them.
216
+ if unit. contains ( . useBytes) && byteCount == 1 {
217
+ unitsToUse. append ( . byte)
218
+ } else if unit. contains ( . useBytes) {
219
+ unitsToUse. append ( . bytes)
220
+ }
221
+ if unit. contains ( . useKB) {
222
+ unitsToUse. append ( . KB)
223
+ }
224
+ if unit. contains ( . useMB) {
225
+ unitsToUse. append ( . MB)
226
+ }
227
+ if unit. contains ( . useGB) {
228
+ unitsToUse. append ( . GB)
229
+ }
230
+ if unit. contains ( . useTB) {
231
+ unitsToUse. append ( . TB)
232
+ }
233
+ if unit. contains ( . usePB) {
234
+ unitsToUse. append ( . PB)
235
+ }
236
+ if unit. contains ( . useEB) {
237
+ unitsToUse. append ( . EB)
238
+ }
239
+ if unit. contains ( . useZB) {
240
+ unitsToUse. append ( . ZB)
241
+ }
242
+ if unit. contains ( . useYBOrHigher) {
243
+ unitsToUse. append ( . YB)
244
+ }
245
+
246
+
247
+ var counter = 0
248
+ for _ in unitsToUse {
249
+ counter += 1
250
+ if counter > unitsToUse. count - 1 {
251
+ counter = unitsToUse. count - 1
252
+ }
253
+ /*
254
+ The units are appended to the array in asceding order, so if the value for byteCount is smaller than the byteSize value of the next unit
255
+ in the Array we use the previous unit. e.g. if byteCount = 1000, and AllowedUnits = [.useKB, .useGB] check to see if byteCount is smaller
256
+ than a GB in bytes(pow(1000, 3)) and if so, we'll use the previous unit which is KB in this case.
257
+ */
258
+ if byteCount < byteSize [ unitsToUse [ counter] ] ! {
259
+ return divide ( byteCount, by: byteSize, for: unitsToUse [ counter - 1 ] )
260
+ }
261
+ }
262
+ return divide ( byteCount, by: byteSize, for: unitsToUse [ counter] )
189
263
}
190
264
191
265
// Coverts the number of bytes to the correct value given a specified unit, then passes the value and unit to formattedValue
@@ -200,36 +274,34 @@ open class ByteCountFormatter : Formatter {
200
274
//Formats the byte value using the NumberFormatter class based on set properties and the unit passed in as a parameter.
201
275
private func formatNumberFor( bytes: Double , unit: Unit ) -> String {
202
276
203
- numberFormatter. numberStyle = . decimal
204
-
205
277
switch ( zeroPadsFractionDigits, isAdaptive) {
206
278
//zeroPadsFractionDigits is true, isAdaptive is true
207
279
case ( true , true ) :
208
280
switch unit {
209
281
case . bytes, . byte, . KB:
210
- numberFormatter. minimumFractionDigits = 0
211
- numberFormatter. maximumFractionDigits = 0
212
- let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
213
- return partsToIncludeFor ( value: result!, unit: unit)
282
+ let result = String ( format: " %.0f " , bytes)
283
+ return partsToIncludeFor ( value: result, unit: unit)
214
284
case . MB:
215
- numberFormatter. minimumFractionDigits = 1
216
- numberFormatter. maximumFractionDigits = 1
217
- let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
218
- return partsToIncludeFor ( value: result!, unit: unit)
285
+ let result = String ( format: " %.1f " , bytes)
286
+ return partsToIncludeFor ( value: result, unit: unit)
219
287
default :
220
- numberFormatter. minimumFractionDigits = 2
221
- numberFormatter. maximumFractionDigits = 2
222
- let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
223
- return partsToIncludeFor ( value: result!, unit: unit)
288
+ let result = String ( format: " %.2f " , bytes)
289
+ return partsToIncludeFor ( value: result, unit: unit)
224
290
}
225
291
//zeroPadsFractionDigits is true, isAdaptive is false
226
292
case ( true , false ) :
227
293
if unit == . byte || unit == . bytes {
228
- return partsToIncludeFor ( value: " \( bytes) " , unit: unit)
294
+ numberFormatter. maximumFractionDigits = 0
295
+ let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
296
+ return partsToIncludeFor ( value: result!, unit: unit)
229
297
} else {
230
- numberFormatter. usesSignificantDigits = true
231
- numberFormatter. minimumSignificantDigits = 3
232
- numberFormatter. maximumSignificantDigits = 3
298
+ if lengthOfInt ( number: Int ( bytes) ) == 3 {
299
+ numberFormatter. maximumFractionDigits = 1
300
+ } else {
301
+ numberFormatter. usesSignificantDigits = true
302
+ numberFormatter. maximumSignificantDigits = 3
303
+ numberFormatter. minimumSignificantDigits = 3
304
+ }
233
305
let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
234
306
return partsToIncludeFor ( value: result!, unit: unit)
235
307
}
@@ -247,25 +319,54 @@ open class ByteCountFormatter : Formatter {
247
319
let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
248
320
return partsToIncludeFor ( value: result!, unit: unit)
249
321
default :
250
- numberFormatter. minimumFractionDigits = 0
251
- numberFormatter. maximumFractionDigits = 2
252
- let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
253
- return partsToIncludeFor ( value: result!, unit: unit)
322
+ let result : String
323
+ //Need to add in an extra case for negative numbers as NumberFormatter formats 0.005 to 0 rather than
324
+ // 0.01
325
+ if bytes < 0 {
326
+ let negBytes = round ( bytes * 100 ) / 100
327
+ result = numberFormatter. string ( from: NSNumber ( value: negBytes) ) !
328
+ } else {
329
+ numberFormatter. minimumFractionDigits = 0
330
+ numberFormatter. maximumFractionDigits = 2
331
+ result = numberFormatter. string ( from: NSNumber ( value: bytes) ) !
332
+ }
333
+
334
+
335
+ return partsToIncludeFor ( value: result, unit: unit)
254
336
}
255
337
//zeroPadsFractionDigits is false, isAdaptive is false
256
338
case ( false , false ) :
257
339
if unit == . byte || unit == . bytes {
258
- return partsToIncludeFor ( value: " \( bytes) " , unit: unit)
340
+ numberFormatter. minimumFractionDigits = 0
341
+ numberFormatter. maximumFractionDigits = 0
342
+ let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
343
+ return partsToIncludeFor ( value: result!, unit: unit)
259
344
} else {
260
- numberFormatter. usesSignificantDigits = true
261
- numberFormatter. minimumSignificantDigits = 3
262
- numberFormatter. maximumSignificantDigits = 3
345
+ if lengthOfInt ( number: Int ( bytes) ) > 3 {
346
+ numberFormatter. maximumFractionDigits = 0
347
+ } else {
348
+ numberFormatter. usesSignificantDigits = true
349
+ numberFormatter. maximumSignificantDigits = 3
350
+ }
263
351
let result = numberFormatter. string ( from: NSNumber ( value: bytes) )
264
352
return partsToIncludeFor ( value: result!, unit: unit)
265
353
}
266
354
}
267
355
}
268
356
357
+ // A helper method to return the length of an int
358
+ private func lengthOfInt( number: Int ) -> Int {
359
+ var num = abs ( number)
360
+ var length : [ Int ] = [ ]
361
+
362
+ while num > 0 {
363
+ let remainder = num % 10
364
+ length. append ( remainder)
365
+ num /= 10
366
+ }
367
+ return length. count
368
+ }
369
+
269
370
// Returns the correct string based on the includesValue and includesUnit properties
270
371
private func partsToIncludeFor( value: String , unit: Unit ) -> String {
271
372
if includesActualByteCount, includesUnit, includesCount {
@@ -276,7 +377,11 @@ open class ByteCountFormatter : Formatter {
276
377
} else if includesCount, includesUnit {
277
378
return " \( value) \( unit) "
278
379
} else if includesCount, !includesUnit {
279
- return " \( value) "
380
+ if value == " Zero " , allowedUnits == . useDefault {
381
+ return " 0 "
382
+ } else {
383
+ return value
384
+ }
280
385
} else if !includesCount, includesUnit {
281
386
return " \( unit) "
282
387
} else {
@@ -298,10 +403,9 @@ open class ByteCountFormatter : Formatter {
298
403
case YB
299
404
}
300
405
// Maps each unit to it's corresponding value in bytes for decimal
301
- private let decimalByteSize : [ Unit : Double ] = [ . byte: 1 , . KB: 1000 , . MB: pow ( 1000 , 2 ) , . GB: pow ( 1000 , 3 ) , . TB: pow ( 1000 , 4 ) , . PB: pow ( 1000 , 5 ) , . EB: pow ( 1000 , 6 ) , . ZB: pow ( 1000 , 7 ) , . YB: pow ( 1000 , 8 ) ]
406
+ private let decimalByteSize : [ Unit : Double ] = [ . byte: 1 , . bytes : 1 , . KB: 1000 , . MB: pow ( 1000 , 2 ) , . GB: pow ( 1000 , 3 ) , . TB: pow ( 1000 , 4 ) , . PB: pow ( 1000 , 5 ) , . EB: pow ( 1000 , 6 ) , . ZB: pow ( 1000 , 7 ) , . YB: pow ( 1000 , 8 ) ]
302
407
303
408
// Maps each unit to it's corresponding value in bytes for binary
304
- private let binaryByteSize : [ Unit : Double ] = [ . byte: 1 , . KB: 1024 , . MB: pow ( 1024 , 2 ) , . GB: pow ( 1024 , 3 ) , . TB: pow ( 1024 , 4 ) , . PB: pow ( 1024 , 5 ) , . EB: pow ( 1024 , 6 ) , . ZB: pow ( 1024 , 7 ) , . YB: pow ( 1024 , 8 ) ]
409
+ private let binaryByteSize : [ Unit : Double ] = [ . byte: 1 , . bytes : 1 , . KB: 1024 , . MB: pow ( 1024 , 2 ) , . GB: pow ( 1024 , 3 ) , . TB: pow ( 1024 , 4 ) , . PB: pow ( 1024 , 5 ) , . EB: pow ( 1024 , 6 ) , . ZB: pow ( 1024 , 7 ) , . YB: pow ( 1024 , 8 ) ]
305
410
306
- }
307
-
411
+ }
0 commit comments