[iOS]Cell, UIViewに影・丸み・輪郭線を追加すると角が影の上にはみ出てしまう / 影の表示のパフォーマンス改善


はみ出てしまう事象

ポイントとしては、影を設定したLayer(が属するView)の上にさらにCellのcontentViewなど別のUIViewがあるとその角が影の上にはみ出してしまいます。

cell自体に影を設定した場合は、cellの上のcontentViewのlayer.masksToBoundstrueとすることで、はみ出しを防止できます。

UIView+Layer.swift
import UIKit

public extension UIView {
    /// 丸みの設定
    func setUpRoundness(_ cornerRadius: CGFloat = 5.0) {
        layer.cornerRadius = cornerRadius
    }

    /// 輪郭線の設定
    func setUpBorderLine() {
        layer.borderColor = ColorName.lightGrayMinus.color.cgColor
        layer.borderWidth = 0.5
    }

    /// 影の設定
    func setUpShadow(_ shadowCornerRadius: CGFloat = 5.0) {
        // ビューの範囲外に影を表示する
        layer.masksToBounds = false
        // 下・右
        layer.shadowOffset = CGSize(width: 3.0, height: 3.0)
        // ぼかし
        layer.shadowRadius = 3
        // 不透明度
        layer.shadowOpacity = 0.1
        // 色
        layer.shadowColor = UIColor.black.cgColor
        // 描画速度 最適化
        let radius = shadowCornerRadius
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
        layer.shouldRasterize = true
        layer.rasterizationScale = UIScreen.main.scale
    }
}

public extension UICollectionViewCell {
    /// UICollectionViewCellの影の設定
    func setUpCellShadow(_ shadowCornerRadius: CGFloat = 0.5) {
        // 影の上にコンテンツビューの角がはみ出ることの防止
        contentView.layer.masksToBounds = true

        super.setUpShadow(shadowCornerRadius)
    }
}

使い方

ViewController.swift
    /// セルが表示される直前に呼ばれる
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        // contentViewに丸みを足す
        contentView.setUpRoundness()

        // contentViewに輪郭線を追加
        contentView.setUpBorderLine()

        // cellのboundsが正しい値になるタイミングで、影を設定
        cell.setUpCellShadow()
    }

影の表示パフォーマンス

layer.shadowPath = UIBezierPath(を設定することで影の表示箇所を毎回計算しなくなるので、軽くなります。必ずやった方がいいと思われます。設定しないと、XcodeのDebug View Hierarcy を使った際に警告が出ていました。

さらにview.layer.shouldRasterizeをtrueとすると、レイヤがbitmap表示され、さらに早くなるとのことです。ただレイヤー内の変更をアニメーションすると、かえって逆効果となるので、レイヤー内で何もアニメーションがない場合はこちらはtrueにすると良さそうです。

参考

StackOverFlow - UITableViewCell: rounded corners and shadow
teratail - UITableViewのcellにpadding, radius, shadowを入れたい
StackOverFlow - When should I set layer.shouldRasterize to YES
UIViewの角丸と影のおはなし