The accepted answer already covers the specific situation of the OP (cell colors in a table view), whereas this answer will approach the more general question title:
How to loop through array and start again if the array is at the end?
The modulo operation naturally comes to mind,
truncatedIndex = runningIndexDividend % divisor
But what if we're to use this in a long running application, where the runningIndexDividend needlessly increase to values much larger than the divisor (possibly, in a theoretic contrived situation, even leading to integer overflow ...)? For such a case, an alternative, mainly to tap into the Sequence neatness of Swift, is to use an on-the-fly generating sequence: one which lazily constructs its next element upon demand.
Using the global sequence(state:next:) function
For the case of constructing a (infinite) sequence which repeatedly traverses a given array ("joining" the head with the tail), you could make use of the global sequence(state:next:) function.
E.g., applied to your example (here storing the colorPalet as a static member of ColorSettings utility class, just know the possible non-thread safety if using static properties in threaded applications):
class ColorSettings {
private static let colorPalet = [
UIColor(red: 255.0/255.0, green: 159.0/255.0, blue: 112.0/255.0, alpha: 1),
UIColor(red: 81.0/255.0, green: 218.0/255.0, blue: 168.0/255.0, alpha: 1),
UIColor(red: 2.0/255.0, green: 207.0/255.0, blue: 255.0/255.0, alpha: 1),
UIColor(red: 144.0/255.0, green: 153.0/255.0, blue: 166.0/255.0, alpha: 1)
]
static let colorSequence = sequence(
state: 1,
next: { (idx: inout Int) -> UIColor? in
guard colorPalet.count > 0 else { return nil }
defer { idx == colorPalet.count ? (idx = 1) : (idx += 1) }
/* alternatively (loose clarity/semantics to gain brevity)
defer { idx = idx % colorPalet.count + 1 } */
return colorPalet[idx-1]
})
}
Example "usage" (not really intended for this application)
// example usage
let numberOfRowsInSection = 7
for (row, color) in zip(0..<numberOfRowsInSection,
ColorSettings.colorSequence) {
// ...
print(row, color)
} /* 0 UIExtendedSRGBColorSpace 1 0.623529 0.439216 1
1 UIExtendedSRGBColorSpace 0.317647 0.854902 0.658824 1
2 UIExtendedSRGBColorSpace 0.00784314 0.811765 1 1
3 UIExtendedSRGBColorSpace 0.564706 0.6 0.65098 1
4 UIExtendedSRGBColorSpace 1 0.623529 0.439216 1
5 UIExtendedSRGBColorSpace 0.317647 0.854902 0.658824 1
6 UIExtendedSRGBColorSpace 0.00784314 0.811765 1 1 */
Note that the state will not be saved between two separate traversals of colorSequence. I.e., if copying the loop above and applying elsewhere, the first state will always correspond to the first color.
Also beware that when constructing an infinitely generating sequence as the one above, the sequence can naturally not terminate by itself (no nil return, apart from the empty colorPalet array case). Hence its practical use will mostly be in conjunction with a finite sequence with use of zip as above.
Using an external state property with AnyIterator
If you'd rather keep the end state in one traversal as a starting point for the subsequent one (not resetting it, as above), you could use an approach similar to the one above, but using a help state property combined with AnyIterator:
class ColorSettings {
private static let colorPalet = [
UIColor(red: 255.0/255.0, green: 159.0/255.0, blue: 112.0/255.0, alpha: 1),
UIColor(red: 81.0/255.0, green: 218.0/255.0, blue: 168.0/255.0, alpha: 1),
UIColor(red: 2.0/255.0, green: 207.0/255.0, blue: 255.0/255.0, alpha: 1),
UIColor(red: 144.0/255.0, green: 153.0/255.0, blue: 166.0/255.0, alpha: 1)
]
private static var idx: Int = 1
static let colorIterator: AnyIterator<UIColor> = AnyIterator {
guard colorPalet.count > 0 else { return nil }
defer { idx == colorPalet.count ? (idx = 1) : (idx += 1) }
return colorPalet[idx-1]
}
}
Example usage:
// first traversal
for (i, color) in zip(0..<2, ColorSettings.colorIterator) {
// ...
print(i, color)
} /* 0 UIExtendedSRGBColorSpace 1 0.623529 0.439216 1
1 UIExtendedSRGBColorSpace 0.317647 0.854902 0.658824 1 */
// state from previous traversal will be used
// to decide starting state here, in next
for (i, color) in zip(0..<4, ColorSettings.colorIterator) {
// ...
print(i, color)
} /* 0 UIExtendedSRGBColorSpace 0.00784314 0.811765 1 1
1 UIExtendedSRGBColorSpace 0.564706 0.6 0.65098 1
2 UIExtendedSRGBColorSpace 1 0.623529 0.439216 1
3 UIExtendedSRGBColorSpace 0.317647 0.854902 0.658824 1 */
cellForRowmethod since you end up recreating this fixed array over and over and over.