web-dev-qa-db-ja.com

UITableViewCellを選択すると、UIView backgroundColorが消えます

インターフェイスビルダーに簡単なtableViewCellビルドがあります。画像を含むUIViewが含まれています。セルを選択すると、デフォルトの青色の選択背景が表示されますが、UIViewのbackgroundColorはなくなりました。

UITableViewCellの実装ファイルは特別なことは何もしません。それはただ初期化され、自己を返し、setSelectedで行うことはすべてsuperを呼び出すことだけです。

TableViewが選択されたときにUIView backgroundColorを表示するにはどうすればよいですか?

69
P5ycH0

ここでの問題は、[スーパー]実装の

- (void) setSelected:(BOOL) selected animated:(BOOL) animated;

uITableViewCellのすべての背景色をrgba(0,0,0,0)に設定します。どうして?おそらく私たちすべてに汗をかかせますか?

ビュー全体が消えるわけではありません(ビューレイヤーの境界線プロパティを変更すると、それらは保持されます)。

以下は、セルに触れた結果の関数呼び出しのシーケンスです

  1. setHighlighted
  2. touchesEnded
  3. layoutSubviews
  4. willSelectRowAtIndexPath(デリゲート側)
  5. setSelected(!!!これは、すべてのビューの背景色が消えるように指示される場所です)
  6. didSelectRowAtIndexPath(デリゲート側)
  7. setSelected(再度)(興味深いことに、この呼び出しでは背景色がクリアされません。そのスーパーメソッド内で奇妙なことが起こっていますか?)
  8. layoutSubviews(再び)

あなたのオプションは

  1. オーバーライド-(void)setSelected:(BOOL)selected animated:(BOOL)animated;呼び出すことなく[super setSelected:selected animated:animated]これにより、a)コードがUITableViewCellサブクラス内にラップされ、b)必要な場合にのみ呼び出されるため(必要な場合は2回ですが、おそらく回避策があるため)、最も技術的に正しい実装が得られます。欠点は、setSelectedのすべての必要な機能(不要なカラークリア機能とは対照的に)を再実装する必要があることです。今すぐsetSelectedを適切にオーバーライドする方法を尋ねないでください。あなたの推測は今のところ私のものと同じくらいです(辛抱強く、私が理解したらこの答えを編集します)。
  2. didSelectRowAtIndexPathで背景色を再度アサートします。インスタンスの外にインスタンスコードを配置するため、これはそれほど優れていません。それとは対照的に、必要なときにだけ呼び出されるという利点があります...
  3. layoutSubviewsで背景色を再度アサートします。 layoutSubviewsは100万回のように呼び出されるため、これはまったく素晴らしいことではありません。テーブルが更新されるたび、スクロールされるたび、祖母がパーマを取得するたびに呼び出されます。つまり、バックグラウンドでの不必要な再アサーションが多く、余分な処理オーバーヘッドが大量に発生します。明るい面では、UITableViewCellサブクラス内にコードを配置します。これはNiceです。

残念ながら、setHighlightedで背景色を再アサートしても、setSelectedの最初の呼び出しですべての背景色が[r:0 b:0 g:0 a:0]に設定される前にsetHighlightedが呼び出されるため、何も実行されません。

// TODO:setSelectedをオーバーライドする方法の素晴らしい説明を提供します(お楽しみに)

107
Brooks
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    UIColor *backgroundColor = self.channelImageView.backgroundColor;
    [super setHighlighted:highlighted animated:animated];
    self.channelImageView.backgroundColor = backgroundColor;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    UIColor *backgroundColor = self.channelImageView.backgroundColor;
    [super setSelected:selected animated:animated];
    self.channelImageView.backgroundColor = backgroundColor;
}
70
mientus

以前は@ P5ycH0が言ったように(1x1画像が引き伸ばされていました)、@ Brooksに続いて、カスタムUITableViewCell実装で-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animatedをオーバーライドし、[super setHighlighted:highlighted animated:animated];を呼び出した後に背景色をリセットすると、セルが選択/強調表示されているときの背景色

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
    [super setHighlighted:highlighted animated:animated];
    myView.backgroundColor = myColor;
}
16
ZeCodea

UITableViewCellが選択されている場合、注意が必要な状態はHighlightedSelectedの2つです。

したがって、UITableViewCellのサブクラスであるカスタムセルクラスがあるシナリオでは、これらの2つのメソッドを簡単にオーバーライドして、この状況を回避できます(Swift):

class MyCell: UITableViewCell {

    @IBOutlet var myView: UIView!

    override func setHighlighted(highlighted: Bool, animated: Bool) {
        let myViewBackgroundColor = myView.backgroundColor
        super.setHighlighted(highlighted, animated: animated)
        myView.backgroundColor = myViewBackgroundColor
    }

    override func setSelected(selected: Bool, animated: Bool) {
        let myViewBackgroundColor = myView.backgroundColor
        super.setSelected(selected, animated: animated)
        myView.backgroundColor = myViewBackgroundColor
    }

}
15
kubilay

ブルックスは、これがなぜ起こるのかについて素晴らしい説明をしていますが、私はより良い解決策があると思います。

サブビューで、setBackgroundColor:好きな色に。セッターは引き続き呼び出されますが、指定した色のみが適用されます。

- (void)setBackgroundColor:(UIColor *)backgroundColor {
    [super setBackgroundColor:[UIColor whiteColor]];
}
4
lavoy

カスタムセルの次の2つのメソッドをオーバーライドする必要があります。

- (void) setSelected:(BOOL)selected animated:(BOOL)animated;
- (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated;

ご了承ください:

  • カスタム実装または対応するメソッドの最初で[super setSelected:animated:][super setHighlighted:animated:]を呼び出す必要があります。
  • カスタムセルのUITableViewCellSelectionStyleNone selectionStyleを設定して、デフォルトのUITableViewCellスタイルを無効にする必要があります。

ここに実装の例:

- (void) setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];
    [self setHighlightedSelected:highlighted animated:animated];
}

- (void) setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
    [self setHighlightedSelected:selected animated:animated];
}

- (void) setHighlightedSelected:(BOOL)selected animated:(BOOL)animated
{
    void(^selection_block)(void) =
    ^
    {
        self.contentView.backgroundColor = selected ? SELECTED_BACKGROUND_COLOR : NORMAL_BACKGROUND_COLOR;
    };

    if(animated)
    {
        [UIView animateWithDuration:SELECTION_ANIMATION_DURATION
                              delay:0.0
                            options:UIViewAnimationOptionBeginFromCurrentState
                         animations:selection_block
                         completion:NULL];
    }
    else
        selection_block();
}

contentViewは、iOS 7で登場したUITableViewCellのプロパティです。代わりに、独自のセルの子ビューを使用できることに注意してください。

3
Igor Vasilev

この問題は、iOS 13で(最終的に)解決される可能性があります。iOS13ベータ3リリースノートでこの甘い段落を見つけました。

UITableViewCellクラスは、セルが強調表示または選択されたときにcontentViewおよびそのサブビューのbackgroundColorまたはisOpaqueプロパティを変更しなくなりました。 contentView内(および含む)のセルのサブビューに不透明なbackgroundColorを設定している場合、セルが強調表示または選択されたときの外観が影響を受ける可能性があります。サブビューの問題を解決する最も簡単な方法は、backgroundColorがnilまたはclearに設定され、opaqueプロパティがfalseであることを確認することです。ただし、必要に応じて、setHighlighted(:animated :)およびsetSelected(:animated :)メソッドをオーバーライドして、移動時にサブビューのこれらのプロパティを手動で変更できます。強調表示され選択された状態との間で。 (13955336)

https://developer.Apple.com/documentation/ios_ipados_release_notes/ios_ipados_13_beta_3_release_notes

3
PatrickDotStar

OK、UIViewクラスの背景色を失うことは、選択されたtableviewcellでの通常の動作です。それを防ぐ方法がわかりませんでした。これで、UIViewを、1x1の引き伸ばされた白いピクセルを含むUIImageViewに置き換えました。 glyいimoですが、動作します。

3
P5ycH0

@Brooksの答えに関連して、これはSwiftおよびiOS8/iOS9で動作するようにしたことです。

  • setSelectedおよびsetHighlightedをオーバーライドします
  • スーパーに電話しないでください
  • contentView.backgroundColor。これはセルの幅全体に広がる必要がないためです(つまり、アクセサリ)。
  • セル自体のbackgroundColorを使用し、それに応じて設定します。

    class AwesomeTableViewCell: UITableViewCell {
    
        private struct Constants {
    
            static var highlightedColor = UIColor.greenColor()
            static var selectedColor = UIColor.redColor()
    
            static let animationTime = NSTimeInterval(0.2)
        }
    
        override func awakeFromNib() {
            super.awakeFromNib()
    
            contentView.backgroundColor = UIColor.clearColor()
            backgroundColor = AppContext.sharedInstance.theme.colors.background
        }
    
        override func setHighlighted(highlighted: Bool, animated: Bool) {
            if animated {
                UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in
                    self.setHighlighted(highlighted)
                })
            } else {
                self.setHighlighted(highlighted)
            }
        }
    
        override func setSelected(selected: Bool, animated: Bool) {
    
            if animated {
                UIView.animateWithDuration(Constants.animationTime, animations: { () -> Void in
                    self.setSelected(selected)
                })
            } else {
                self.setSelected(selected)
            }
        }
    
        private func setHighlighted(highlighted: Bool) {
    
            backgroundColor = highlighted ? Constants.highlightedColor : UIColor.whiteColor()
        }
    
        private func setSelected(selected: Bool) {
    
            backgroundColor = selected ? Constants.selectedColor : UIColor.whiteColor()
        }
    }
    
1
Kevin R

これをUITableViewCellに追加します

override func setHighlighted(highlighted: Bool, animated: Bool) {
    super.setHighlighted(false, animated: animated)
    if highlighted {
        self.backgroundColor = UIColor.blueColor()
    }else{
        UIView.animateWithDuration(0.2, animations: {
            self.backgroundColor = UIColor.clearColor()
        })
    }
}
1
Tanel Teemusk

概要

このソリューションでは、セルの背景色の一部をロックできますが、残りはシステムの動作によって制御されます。


Mientusの answer に基づいて、背景色を保持するビューを指定するできるソリューションを作成しました。

これにより、他のセルのサブビューのハイライト/選択時に背景を削除することができ、これがこのケースで機能する唯一のソリューションです(2つのビューには永続的な背景が必要です)。

ロックするビューのリストを含むBackgroundLockableプロトコルを使用して、色を保持しながらクロージャーを実行するプロトコル指向のアプローチを使用しました。

protocol BackgroundLockable {
    var lockedBackgroundViews: [UIView] { get }
    func performActionWithLockedViews(_ action: @escaping () -> Void)
}

extension BackgroundLockable {
    func performActionWithLockedViews(_ action: @escaping () -> Void) {
        let lockedViewToColorMap = lockedBackgroundViews.reduce([:]) { (partialResult, view) -> [UIView: UIColor?] in
            var mutableResult = partialResult
            mutableResult[view] = view.backgroundColor
            return mutableResult
        }

        action()

        lockedViewToColorMap.forEach { (view: UIView, color: UIColor?) in
            view.backgroundColor = color
        }
    }
}

次に、UITableViewCellのサブクラスがあります。これは、デフォルトの(スーパー)動作を呼び出すことでプロトコルのクロージャーを実行するために、強調表示と選択をオーバーライドします。

class LockableBackgroundTableViewCell: UITableViewCell, BackgroundLockable {

    var lockedBackgroundViews: [UIView] {
        return []
    }

    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
        performActionWithLockedViews {
            super.setHighlighted(highlighted, animated: animated)
        }
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        performActionWithLockedViews {
            super.setSelected(selected, animated: animated)
       }
    }
}

これで、いくつかのセルにロック動作を簡単に追加するには、LockableBackgroundTableViewCellをサブクラス化するか、セルクラスでBackgroundLockableプロトコルを使用するだけです。

class SomeCell: LockableBackgroundTableViewCell {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var icon: UIImageView!
    @IBOutlet weak var button: UIButton!

    override var lockedBackgroundViews: [UIView] {
        return [label, icon]
    }
}
1
Yasir

Swift 4

UITableViewCellクラスで:

override func setSelected(_ selected: Bool, animated: Bool) {
    myView.backgroundColor = UIColor.blue
}

override func setHighlighted(_ highlighted: Bool, animated: Bool) {
    myView.backgroundColor = UIColor.blue
}
1
Channel

この奇妙な問題に少し時間を費やしただけです。行が選択されたときに素敵なアニメーションを保持するために、UITableViewCellSelectionStyleNoneスタイルを設定したくありませんでした。しかし、提案されたアイデアはどれもうまくいきませんでした-私はsetSelectedとsetHighlightedをオーバーライドし、そこでサブビューbackgroundColorを設定しようとしました-iOSによってリセットし続け、まだ点滅しています(新しい色->古い色)。私にとっては、修正は非常に簡単でした。行が選択されると、別のView Controllerがプッシュされ、ユーザーはその画面でオプションを選択し、ユーザーの選択に基づいて色を変更するデリゲートが呼び出されます。このデリゲートでは、セルに対して[cell setSelected:NO animated:NO]を実行します。 (静的なUITableViewControllerがあり、セルへのアウトレットがあります)。おそらくdidSelectメソッドでセルの選択を解除できますが、私の場合はセグエを使用しています。

0
dimaclopin

これが私の見解です。私はすべてのセルが継承するサブクラスを持っているので、UIImageViewsの背景の変更を避けるためにそれを行う方法です:

    override func setHighlighted(highlighted: Bool, animated: Bool) {
    var backgroundColors = [UIView: UIColor]()

    for view in contentView.subviews as [UIView] {
        if let imageView = view as? UIImageView {
            backgroundColors[imageView] = imageView.backgroundColor
        }
    }

    super.setHighlighted(highlighted, animated: animated)

    for view in contentView.subviews as [UIView] {
        if let imageView = view as? UIImageView {
            imageView.backgroundColor = backgroundColors[imageView]
        }
    }
}

override func setSelected(selected: Bool, animated: Bool) {
    var backgroundColors = [UIView: UIColor]()

    for view in contentView.subviews as [UIView] {
        if let imageView = view as? UIImageView {
            backgroundColors[imageView] = imageView.backgroundColor
        }
    }

    super.setSelected(selected, animated: animated)

    for view in contentView.subviews as [UIView] {
        if let imageView = view as? UIImageView {
            imageView.backgroundColor = backgroundColors[imageView]
        }
    }
}

これにより、すべてのUIImageViewの問題が自動的に修正されます。

0
allaire

IOS 7では、setSelected:animated:UITableViewCellサブクラスですが、@ Brooksのヒントに反して、[super setSelected:selected animated:animated]

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Reassert the background color of the color view so that it shows
    // even when the cell is highlighted for selection.
    self.colorView.backgroundColor = [UIColor blueColor];
}

これにより、ユーザーがセルをタップしたときにシステムのデフォルトの選択アニメーションを保持し、テーブルビューデリゲートのdidSelectRowAtIndexPath:

0
Matthew Quiros

UITableViewCellクラスの関数setHighlightedをオーバーライドすることで、tableViewCellの動作を変更できます(これを継承する必要があります)。セルの背景画像を変更するコードの例:

// animate between regular and highlighted state
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated; {
    [super setHighlighted:highlighted animated:animated];

    //Set the correct background Image
    UIImageView* backgroundPicture = (UIImageView*)[self viewWithTag:HACK_BACKGROUND_VIEW_TAG];
    if (highlighted) {
        backgroundPicture.image = [UIImage imageNamed:@"FondSelected.png"]; 
    }
    else {
        backgroundPicture.image = [UIImage imageNamed:@"Fond.png"]; 
    }
}

インターフェイスビルダーで選択モードをグレー、ブルー、またはなしに変更することもできます。

0
CedricSoubrie

IBを使用してtableViewCellを構築したということから、ビューをcontentViewではなく、UITableViewCellのviewのサブビューとして追加しているかどうかを確認したいと思います。コンテンツビューは、セルに表示されるコンテンツのデフォルトのスーパービューです。

参照から:

UITableViewCellオブジェクトのコンテンツビューは、セルによって表示されるコンテンツのデフォルトのスーパービューです。追加のビューを追加するだけでセルをカスタマイズする場合は、セルが編集モードに移行したり編集モードから移行したりするときに適切に配置されるように、コンテンツビューに追加する必要があります。

0
MHC