0

I have an array of colors, that I want to apply to uitableviewcells in iOS.

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)
        ]
        cell.backgroundColor = colorPalet[indexPath.row]

The problem is, then when indexPath.row is greater then the colorPalet array, it will crash, because there is no more entries in the array. How to start iteratie again through the array if it is at the end of the array in Swift?

1
  • 1
    It's pretty inefficient to use a plain local constant for the colors in the cellForRow method since you end up recreating this fixed array over and over and over. Commented Oct 29, 2016 at 20:04

5 Answers 5

5

you can use modulo:

cell.backgroudColor = colorPalet[indexPath.row % colorPalet.count]
Sign up to request clarification or add additional context in comments.

1 Comment

Really nice. Thanks!
1

Make colorPalet an instance variable. You can just move the code below to the top of the class:

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)
        ]

That way you're not creating an array of colors for each cell you configure. Then use the modulo (`%) Code in ROC's answer:

cell.backgroudColor = colorPalet[indexPath.row % colorPalet.count]

Comments

0
for index in 0..array.count {

    //Do stuff here
    if (index == array.count -1)
    {
        index = 0
    }
}

1 Comment

index is a constant, so I can't change it.. How to solve that?
0

You can use something like this:

let index = indexPath.row%4
cell.something = colorPalet[index]

1 Comment

Yes. Ideally I would not have hardcoded it in my project. It was just meant for how to achieve it.
0

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   */

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.