3
\$\begingroup\$

When a UITableViewCell has subviews with background color set, when setSelected(_: animated:) gets called, all the views are made transparent.

This issue is also discussed here

I'm trying to solve this issue by extending UITableViewCell

extension UITableViewCell {
    private func backgroundColors(views: [UIView]) -> [UIColor?] {
        var colors = [UIColor?]()
        for view in views {
            colors.append(view.backgroundColor)
        }
        return colors
    }

    private func resetBackgroundColors(views: [UIView], colors: [UIColor?]) {
        for (index, view) in views.enumerate() {
            view.backgroundColor = colors[index]
        }
    }

    func preserveBackgroundColors(views: [UIView], @noescape f: () -> Void) {
        let colors = backgroundColors(views)
        f()
        resetBackgroundColors(views, colors: colors)
    }
}

class MyCell: UITableViewCell {
    override func setSelected(selected: Bool, animated: Bool) {
        preserveBackgroundColors([/* some views */]) {
            super.setSelected(selected, animated: animated)
            /* rest of method implementation */
        }
    }
}

I'm trying to avoid having this functionality in a class hierarchy.

Is this a good implementation?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

This is a very strange approach for quite a few reasons.

First, the fact that this is an extension of UITableViewCell makes absolutely no sense to me. The method is completely procedural. It takes in an array of views (any views... from any where). From these views, it creates an array of colors to preserve the current background color. Then executes a function, f (which by the way, SwiftLint would never allow a single-letter variable name like this) (and you should definitely be using SwiftLint). And function f may or may not even change the colors of any of these views. And then we use parallel arrays, a very procedural approach, to reset these colors.

And would we even notice the color change at all?

Or is the intent here to hide the color change?


As for a more concrete review of some of the code you have, this function:

private func backgroundColors(views: [UIView]) -> [UIColor?] {
    var colors = [UIColor?]()
    for view in views {
        colors.append(view.backgroundColor)
    }
    return colors
}

Could be replaced with a single line of code.

let backgroundColors = views.map { $0.backgroundColor }

But... to make our code less procedural... why don't we build a tuple array:

let viewColorPairs = views.map { ($0, $0.backgroundColor) }

And then when it comes time to reset the background colors:

for (view, color) in viewColorPairs {
    view.backgroundColor = color
}

So now we've eliminated your first two functions, and your preserveBackgroundColors function is simplified into:

func preserveBackgroundColors(views: [UIView], @noescape closure: () -> Void) {
    let viewColorPairs = views.map { ($0, $0.backgroundColor) }
    closure()
    for (view, color) in viewColorPairs {
        view.backgroundColor = color
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ I like your suggestion to use map and tuples. I'm trying to use map/flatmap etc. more often, but this hadn't occurred to me in this case. To answer your questions: super.setSelected(_: animated) changes the background colors of views to clear, which I very much don't like. preserveBackgroundColors's usefulness is restricted to this method (maybe). That's why I placed it in a UITableViewCell extension. Should I make it a free function instead? \$\endgroup\$ Commented Apr 14, 2016 at 14:26
0
\$\begingroup\$

Depending on what one is trying to achive, one could also preserve views' backgroundColor with .zero lines of code, by setting said view's layer backgroundColor , and keeping its backgroundColor to .clear (e.g. during view creation)

For example, add a flag in a view-creating helper-function to denote where BG color ought to be placed: Swift 4+ code

func makeLabel(_ text:String, back_color:UIColor? = nil, lock_back_color:Bool = false, etc etc) -> UILabel {

    let lbl = UILabel(frame: .zero)
    lbl.text = text

    ...

    if lock_back_color {
            lbl.backgroundColor = UIColor.clear // note 0: set it before layer BG color.
            lbl.layer.backgroundColor = back_color.cgColor // note 1: it's CGColor
     }else{
            lbl.backgroundColor = back_color
     }

    ...

    return lbl
}    

This way, one can prevent the default behaviour of UITableViewCell from setting child-views' background to clear on select/highlight. Indirectly, the said view's back color is some-what private (on the cheap).

\$\endgroup\$

Your Answer

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

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