【问题标题】:UITableViewCell: rounded corners and shadowUITableViewCell:圆角和阴影
【发布时间】:2016-10-05 08:26:29
【问题描述】:

我正在更改 UITableViewCell 的宽度,以便单元格更小,但用户仍然可以沿 tableview 的边缘滚动。

override func layoutSubviews() {        
    // Set the width of the cell
    self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width - 40, self.bounds.size.height)
    super.layoutSubviews()
}

然后我绕过角落:

cell.layer.cornerRadius = 8
cell.layer.masksToBounds = true

到目前为止一切顺利。问题发生在阴影上。边界被掩盖,所以阴影显然不会出现。我查找了其他答案,但似乎无法弄清楚如何沿边界圆角显示阴影。

cell.layer.shadowOffset = CGSizeMake(0, 0)
cell.layer.shadowColor = UIColor.blackColor().CGColor
cell.layer.shadowOpacity = 0.23
cell.layer.shadowRadius = 4

所以我的问题是——如何减小宽度、圆角并同时为 UITableViewCell 添加阴影?

更新:尝试 R Moyer 的回答

【问题讨论】:

  • 您需要启用masksToBounds 吗?您也许可以调整约束以不使用它。否则,如果您只是使单元格的背景清晰并在包含所有单元格视图的单元格内添加另一个 UIView 怎么办。然后,您可以使用圆角和阴影使容器视图更小,而不必担心阴影被剪裁。

标签: swift uitableview


【解决方案1】:

从 iOS 13 开始,您只需要使用样式“UITableViewStyleInsetGrouped”。

【讨论】:

    【解决方案2】:

    假设

    • viewContents = 包含所有视图的视图
    • viewContainer = 它包含 viewContents 的视图,其中前导、尾随、顶部、底部均为零。

    现在的想法是,我们将阴影添加到 viewContainer。并且圆角 viewContents。最重要的是不要忘记将 viewContainer 的背景颜色设置为 nil。

    这是代码 sn-p。

    override func layoutSubviews() {
        super.layoutSubviews()
        //viewContainer is the parent of viewContents
        //viewContents contains all the UI which you want to show actually.
        
        self.viewContents.layer.cornerRadius = 12.69
        self.viewContents.layer.masksToBounds = true
        
        let bezierPath = UIBezierPath.init(roundedRect: self.viewContainer.bounds, cornerRadius: 12.69)
        self.viewContainer.layer.shadowPath = bezierPath.cgPath
        self.viewContainer.layer.masksToBounds = false
        self.viewContainer.layer.shadowColor = UIColor.black.cgColor
        self.viewContainer.layer.shadowRadius = 3.0
        self.viewContainer.layer.shadowOffset = CGSize.init(width: 0, height: 3)
        self.viewContainer.layer.shadowOpacity = 0.3
        
        // sending viewContainer color to the viewContents.
        let backgroundCGColor = self.viewContainer.backgroundColor?.cgColor
        //You can set your color directly if you want by using below two lines. In my case I'm copying the color.
        self.viewContainer.backgroundColor = nil
        self.viewContents.layer.backgroundColor =  backgroundCGColor
      }
    

    这是结果

    【讨论】:

    • 你在每个部分使用一个单元格吗?
    • @SultanAli 是的,我要为每个部分返回一行。
    【解决方案3】:

    关于答案R Moyer,解决方案很好,但是在cellForRowAt方法之后并不总是设置边界,所以作为他解决方案的一个可能的细化,是将setupShadow()方法的调用转移到LayoutSubview () 例如:

    class ShadowView: UIView {
    
        var setupShadowDone: Bool = false
        
        public func setupShadow() {
            if setupShadowDone { return }
            print("Setup shadow!")
            self.layer.cornerRadius = 8
            self.layer.shadowOffset = CGSize(width: 0, height: 3)
            self.layer.shadowRadius = 3
            self.layer.shadowOpacity = 0.3
            self.layer.shadowColor = UIColor.black.cgColor
            self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, 
    byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 8, height: 
    8)).cgPath
            self.layer.shouldRasterize = true
            self.layer.rasterizationScale = UIScreen.main.scale
        
            setupShadowDone = true
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            print("Layout subviews!")
            setupShadow()
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果有用的话,我一直在使用下面的代码来实现,只需要在cellForRowAt中运行即可。

      首先,向 UITableViewCell 添加一个扩展,使您能够在 TableViewCell 上创建阴影和圆角:

      extension UITableViewCell {
          func addShadow(backgroundColor: UIColor = .white, cornerRadius: CGFloat = 12, shadowRadius: CGFloat = 5, shadowOpacity: Float = 0.1, shadowPathInset: (dx: CGFloat, dy: CGFloat), shadowPathOffset: (dx: CGFloat, dy: CGFloat)) {
              layer.cornerRadius = cornerRadius
              layer.masksToBounds = true
              layer.shadowColor = UIColor.black.cgColor
              layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
              layer.shadowRadius = shadowRadius
              layer.shadowOpacity = shadowOpacity
              layer.shadowPath = UIBezierPath(roundedRect: bounds.insetBy(dx: shadowPathInset.dx, dy: shadowPathInset.dy).offsetBy(dx: shadowPathOffset.dx, dy: shadowPathOffset.dy), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)).cgPath
              layer.shouldRasterize = true
              layer.rasterizationScale = UIScreen.main.scale
              
              let whiteBackgroundView = UIView()
              whiteBackgroundView.backgroundColor = backgroundColor
              whiteBackgroundView.layer.cornerRadius = cornerRadius
              whiteBackgroundView.layer.masksToBounds = true
              whiteBackgroundView.clipsToBounds = false
              
              whiteBackgroundView.frame = bounds.insetBy(dx: shadowPathInset.dx, dy: shadowPathInset.dy)
              insertSubview(whiteBackgroundView, at: 0)
          }
      }
      

      然后在 cellForRowAt 中引用这个:

      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
          let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellIdentifier", for: indexPath) as! YourCellClass
      
          cell.addShadow(backgroundColor: .white, cornerRadius: 13, shadowRadius: 5, shadowOpacity: 0.1, shadowPathInset: (dx: 16, dy: 6), shadowPathOffset: (dx: 0, dy: 2))
       
          // Or if you are happy with the default values in your extension, just use this instead:
          // cell.addShadow()
      
          return cell
      }
      

      这是我的结果:

      Screenshot

      【讨论】:

      • 添加下一行并删除最后一行 self.contentView.addSubview(whiteBackgroundView) self.contentView.sendSubviewToBack(whiteBackgroundView)
      • @ben 它不适合我
      【解决方案5】:
          cell.layer.cornerRadius = 0.25
          cell.layer.borderWidth = 0
          cell.layer.shadowColor = UIColor.black.cgColor
          cell.layer.shadowOffset = CGSize(width: 0, height: 0)
          cell.layer.shadowRadius = 5
          cell.layer.shadowOpacity = 0.1
          cell.layer.masksToBounds = false
      

      【讨论】:

        【解决方案6】:

        在单元格的内容视图“backView”内创建一个 UIVIEW 并将 backView 的出口添加到单元格类 然后将这些代码行添加到 awakeFromNib()

        self.backView.layer.cornerRadius = 28
        self.backView.clipsToBounds = true 
        

        圆角半径取决于您的设计... 将这些代码添加到 cellForRowAt 函数中

        cell.layer.shadowColor = UIColor.black.cgColor
        cell.layer.shadowOffset = CGSize(width: 0, height: 0)
        cell.layer.shadowOpacity = 0.6
        

        并将单元格视图背景颜色设置为清除颜色

        不要忘记在单元格的 4 个边与您刚刚在 StoryBoard 中的单元格 contentView 中添加的 backView 之间添加一点空间

        希望你喜欢它

        【讨论】:

          【解决方案7】:

          1- 创建自定义 TableViewCell 类 .将以下代码粘贴到您创建 IBOutlets 的类级别。 exerciseView 是您要舍入到的 ContentView 内部的视图。

          @IBOutlet weak var exerciseView: UIView! {
                  didSet {
                      self.exerciseView.layer.cornerRadius = 15
                      self.exerciseView.layer.masksToBounds = true
                  }
              }
          

          didSet 基本上是变量观察者。您可以在 awakeFromNib 函数中执行此操作,也可以:

          self.exerciseView.layer.cornerRadius = 15
          self.exerciseView.layer.masksToBounds = true
          

          【讨论】:

            【解决方案8】:

            永远不要使用 UIBezierPath , bezierPathWithRect:shadowFrame 等,因为它真的很重,并且会在视图顶部绘制一个图层,并且需要再次重新加载表格视图以确保单元格以正确的方式呈现,有时甚至重新加载可能不会帮助。取而代之的是根据需要使用具有圆形边缘的部分页眉和页脚,并且它位于情节提要内部,这将使表格视图的滚动和加载非常流畅,没有任何渲染问题(有时称为丢失单元格并出现在滚动条上)

            请参阅此处如何为圆角设置不同的整数值:Setting masked corners in Interface Builder

            只需将上述值用于您的部分页眉和页脚。

            【讨论】:

              【解决方案9】:

              接受的答案有效,但添加一个额外的子视图来获得这种效果几乎没有意义。这是有效的解决方案。

              第一步:添加阴影和圆角半径

              // do this in one of the init methods
              override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
                  super.init(style: style, reuseIdentifier: reuseIdentifier)
              
                  // add shadow on cell
                  backgroundColor = .clear // very important
                  layer.masksToBounds = false
                  layer.shadowOpacity = 0.23
                  layer.shadowRadius = 4
                  layer.shadowOffset = CGSize(width: 0, height: 0)
                  layer.shadowColor = UIColor.blackColor().CGColor
              
                  // add corner radius on `contentView`
                  contentView.backgroundColor = .white
                  contentView.layer.cornerRadius = 8
              }
              

              第二步:掩码到willDisplay中的边界

              func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
                  // this will turn on `masksToBounds` just before showing the cell
                  cell.contentView.layer.masksToBounds = true
              }
              

              奖励:平滑滚动

              // if you do not set `shadowPath` you'll notice laggy scrolling
              // add this in `willDisplay` method
              let radius = cell.contentView.layer.cornerRadius
              cell.layer.shadowPath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: radius).cgPath
              

              【讨论】:

              • 这应该是公认的答案。比为阴影添加单独的 UIView 干净得多。谢谢!
              • 这个解决方案在设置 tableviews clipsToBounds = false 后对我有用
              • willDisplay 中设置masksToBounds 为我做了,谢谢!
              • 对我不起作用。视图的边界仍然混乱。猜猜这不是正确的方法。
              • 对我来说,当我将所有属性放入 awakeFromNib() 函数而不是 init() 时,它就起作用了
              【解决方案10】:

              这个问题来得正是时候!我真的只是自己解决了同样的问题。

              1. 在单元格的内容视图中创建一个UIView(我们将其称为mainBackground)。这将包含您单元格的所有内容。定位它并在情节提要中应用必要的约束。
              2. 创建另一个UIView。这个将是带有阴影的那个(我们将其称为shadowLayer)。完全按照您在 mainBackground 中所做的定位,但在其后面,并应用相同的约束。
              3. 现在您应该可以如下设置圆角和阴影了:

                cell.mainBackground.layer.cornerRadius = 8  
                cell.mainBackground.layer.masksToBounds = true
                
                cell.shadowLayer.layer.masksToBounds = false
                cell.shadowLayer.layer.shadowOffset = CGSizeMake(0, 0)
                cell.shadowLayer.layer.shadowColor = UIColor.blackColor().CGColor
                cell.shadowLayer.layer.shadowOpacity = 0.23
                cell.shadowLayer.layer.shadowRadius = 4
                

              但是,这里的问题是:计算每个单元格的阴影是一项缓慢的任务。当您滚动浏览表格时,您会注意到一些严重的延迟。解决此问题的最佳方法是为阴影定义UIBezierPath,然后对其进行光栅化。所以你可能想要这样做:

              cell.shadowLayer.layer.shadowPath = UIBezierPath(roundedRect: cell.shadowLayer.bounds, byRoundingCorners: .AllCorners, cornerRadii: CGSize(width: 8, height: 8)).CGPath
              cell.shadowLayer.layer.shouldRasterize = true
              cell.shadowLayer.layer.rasterizationScale = UIScreen.mainScreen().scale
              

              但这会产生一个新问题! UIBezierPath 的形状取决于 shadowLayer 的边界,但在调用 cellForRowAtIndexPath 时边界未正确设置。因此,您需要根据shadowLayer 的边界调整shadowPath。最好的方法是继承UIView,并在bounds 属性中添加一个属性观察者。然后在didSet 中设置阴影的所有属性。请记住在情节提要中更改 shadowLayer 的类以匹配您的新子类。

              class ShadowView: UIView {
                  override var bounds: CGRect {
                      didSet {
                          setupShadow()
                      }
                  }
              
                  private func setupShadow() {
                      self.layer.cornerRadius = 8
                      self.layer.shadowOffset = CGSize(width: 0, height: 3)
                      self.layer.shadowRadius = 3
                      self.layer.shadowOpacity = 0.3
                      self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 8, height: 8)).cgPath
                      self.layer.shouldRasterize = true
                      self.layer.rasterizationScale = UIScreen.main.scale
                  }
              }
              

              【讨论】:

              • 我设置了视图并应用了约束,但这不起作用。角落仍然是方形的,没有应用阴影。认为可能是因为两个视图都没有颜色,进行了必要的调整但没有骰子。
              • 你是否改变了 Storyboard 中 shadowView 的类?当您将插座连接到您的单元类时,shadowView 的类型应该是ShadowView!
              • 一旦我将 shadowView 和 mainBackground 调整为小于单元格本身就可以工作。谢谢。
              • 哇!难道没有更简单的方法来圆角并为集合视图单元格创建阴影吗?
              • 有史以来最简单、最天才的解决方案之一!
              【解决方案11】:

              无需额外视图即可工作!

              override func awakeFromNib() {
                  super.awakeFromNib()
              
                  layer.masksToBounds = false
                  layer.cornerRadius = Constants.cornerRadius
              }
              
              public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                  ...
                  let layer = cell.layer
                  layer.shadowOffset = CGSize(width: 0, height: 1)
                  layer.shadowRadius = 2
                  layer.shadowColor = UIColor.lightGray.cgColor
                  layer.shadowOpacity = 0.2
                  layer.frame = cell.frame
                  cell.tagLabel.text = tagItems[indexPath.row].text
              
                  return cell
              }
              

              【讨论】:

              【解决方案12】:

              试试这个,它对我有用。

                  cell.contentView.layer.cornerRadius = 5.0
                  cell.contentView.layer.borderColor = UIColor.gray.withAlphaComponent(0.5).cgColor
                  cell.contentView.layer.borderWidth = 0.5
              
              
                  let border = CALayer()
                  let width = CGFloat(2.0)
                  border.borderColor = UIColor.darkGray.cgColor
                  border.frame = CGRect(x: 0, y: cell.contentView.frame.size.height - width, width:  cell.contentView.frame.size.width, height: cell.contentView.frame.size.height)
              
                  border.borderWidth = width
                  cell.contentView.layer.addSublayer(border)
                  cell.contentView.layer.masksToBounds = true
                  cell.contentView.clipsToBounds = true
              

              【讨论】:

              • 老兄!它只是一个边界:|
              【解决方案13】:

              我使用以下代码实现了同样的效果。但是您已将其放在 TableViewCell 子类的 layoutSubviews() 方法中。

              self.contentView.backgroundColor = [UIColor whiteColor];
              self.contentView.layer.cornerRadius = 5;
              self.contentView.layer.shadowOffset = CGSizeMake(1, 0);
              self.contentView.layer.shadowColor = [[UIColor blackColor] CGColor];
              self.contentView.layer.shadowRadius = 5;
              self.contentView.layer.shadowOpacity = .25;
              CGRect shadowFrame = self.contentView.layer.bounds;
              CGPathRef shadowPath = [UIBezierPath bezierPathWithRect:shadowFrame].CGPath;
              self.contentView.layer.shadowPath = shadowPath;
              

              【讨论】:

              • 在您的自定义单元格视图中,在方法 layoutSubviews() 中调用它
              • 我在 contentView 中有一个 containerView,但在单元类中的 layoutSubview 方法中仍然没有呈现阴影。
              【解决方案14】:

              要为单元格创建阴影和角落,您只需要一个 backView。请参阅下面的示例。

              您必须添加backView 并设置leadingtrailingtopbottom 约束等于Content view。 将您的内容发送到 backView 并进行适当的限制,但请确保您的内容不会超出范围r backView

              然后在你的单元初始化代码中添加这些行:

              override func awakeFromNib() {
                  super.awakeFromNib()
              
                  backgroundColor = Colors.colorClear
              
                  self.backView.layer.borderWidth = 1
                  self.backView.layer.cornerRadius = 3
                  self.backView.layer.borderColor = Colors.colorClear.cgColor
                  self.backView.layer.masksToBounds = true
              
                  self.layer.shadowOpacity = 0.18
                  self.layer.shadowOffset = CGSize(width: 0, height: 2)
                  self.layer.shadowRadius = 2
                  self.layer.shadowColor = Colors.colorBlack.cgColor
                  self.layer.masksToBounds = false
              }
              

              不要忘记为 Back View 创建 IBOutlet。

              结果如下:

              【讨论】:

              • 它有效,但它在所有 4 个视图角上显示阴影。如何只添加阴影?
              • 这对我不起作用。阴影仅显示在单元格的底部。你有什么想法吗?
              【解决方案15】:
              cell.layer.cornerRadius = 10
              cell.layer.masksToBounds = true
              

              【讨论】:

              • in cellForItemAt indexPath
              【解决方案16】:

              您可以尝试另一种方法,在UITableViewCell 中输入UIView。将UITableViewCell 的背景颜色设置为清除颜色。现在,您可以制作圆角并在UIVIew 上添加阴影。这看起来好像单元格宽度减小了,用户可以沿着 tableView 的边缘滚动。

              【讨论】:

              • 目前正在测试。
              • 你测试了吗?
              猜你喜欢
              • 2014-09-15
              • 2016-02-20
              • 2011-09-10
              • 1970-01-01
              • 2020-06-27
              • 2011-06-12
              • 2023-04-01
              • 1970-01-01
              相关资源
              最近更新 更多