1
\$\begingroup\$

iOS: prettify random color for debugging

I used to use random color to debug, from colors I like

extension CALayer{
      func debug(){
             let colors = [UIColor.blue, UIColor.magenta, UIColor.red,
                      UIColor.green, UIColor.brown, UIColor.purple,
                      UIColor.cyan]
             if let color = colors.randomElement().cgColor{
                     borderColor = color
              }
       }
 }

The effect case:

The colors are not of regularity. Not very pretty.

111

I want to prettify it. Do some color alignment. The same color for the same UI hierarchy.

achieve something like this.

000

In order to not make colors repeat, and to show more colors in a page. I use ring buffer.

In order to do some color alignment, the same color for the same UI hierarchy,I use call stack trace. Use the call function as the key for its color.

Here is the code:

// the color ring buffer
struct ColorCluster {
    static var shared = ColorCluster()

    let colors = [UIColor.blue, UIColor.magenta, UIColor.red,
                  UIColor.green, UIColor.brown, UIColor.purple,
                  UIColor.cyan]

    var colorRegister = [String: Int]()
    var count = -1

    mutating func get(_ k: String) -> CGColor{
        var index = 0
        if let idx = colorRegister[k]{
            index = idx
        }
        else{
            count += 1
            if count >= colors.count{
                count = 0
            }
            index = count
            colorRegister[k] = count
        }
        return colors[index].cgColor
    }
}


extension CALayer{

    func debug(){
        // call stack trace, remove methods of the system 
        let libs: Set<String> = ["UIKitCore", "QuartzCore", "CoreFoundation"]
        var methods = [String]()
        Out: for symbol in Thread.callStackSymbols{
            for lib in libs {
                if symbol.contains(lib){
                    break Out
                }
            }
            methods.append(symbol)
        }
        var edgeColor = UIColor.black.cgColor   // color place holder
        if let name = methods.last{
            edgeColor = ColorCluster.shared.get(name)
        }
        borderColor = edgeColor
        borderWidth = 1
    }
}

Usage:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

       let cell = tableView.dequeue(for: PracticeDetailCell.self, ip: indexPath)
        cell.layer.debug()
        return cell
}

more code on github

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I've had a look, and think it should be possible to trim down the ColorCluster-logic quite a bit, mostly by tightening up the get(: function a bit. Avoiding some if-elsing and only manipulating the index and count once.

struct ColorCluster {
    static var shared = ColorCluster()
    private init() { }
    
    private let colors: [UIColor] = [.blue, .magenta, .red, .green, .brown, .purple, .cyan]
    private var colorRegister = [String: Int]()
    private var count = 0

    mutating func getColor(for key: String) -> CGColor {
        let countIsOutOfBounds = count >= colors.count
        let nextAvailableIndex = countIsOutOfBounds ? 0 : count
        
        let index = colorRegister[key] ?? nextAvailableIndex
        count = countIsOutOfBounds ? 0 : count + 1
        
        return colors[index].cgColor
    }
}

I also had a quick look at the debug-extension. I haven't actually tested it, but the gist is to go down the functional route when creating the array of method-names:

extension CALayer {
    func debug() {
        // call stack trace, remove methods of the system
        let libs: Set<String> = ["UIKitCore", "QuartzCore", "CoreFoundation"]
        
        let methodNames = Thread.callStackSymbols.reduce([String]()) { names, symbol in
            let containingSymbol = libs.filter({ symbol.contains($0) })
            if containingSymbol.isEmpty {
                return names
            }
            return names + [symbol]
        }
        
        var edgeColor = UIColor.black.cgColor // color place holder
        if let name = methodNames.last {
            edgeColor = ColorCluster.shared.getColor(for: name)
        }
        borderColor = edgeColor
        borderWidth = 1
    }
} 
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.