UITableViewCell: rounded corners and shadow

前端 未结 14 1318
故里飘歌
故里飘歌 2020-12-02 05:05

I\'m changing the width of a UITableViewCell so that the cell is smaller but the user can still scroll along the edges of the tableview.

override func layout         


        
相关标签:
14条回答
  • 2020-12-02 05:16

    Try this, it worked for me.

        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
    
    0 讨论(0)
  • 2020-12-02 05:17

    The accepted answer works but adding an extra subview to get this effect make little to no sense. Here is the solution that works.

    1st step: Add shadow and corner radius

    // 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
    }
    

    2nd step: Mask to bounds in 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
    }
    

    Bonus: Smooth scrolling

    // 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
    
    0 讨论(0)
  • 2020-12-02 05:17

    One alternative approach you can try, take a UIView in UITableViewCell. Set background color of UITableViewCell to clear color. Now, you can make round corners and add shadow on UIVIew. This will appear as if cell width is reduced and user can scroll along the edges of the tableView.

    0 讨论(0)
  • 2020-12-02 05:18

    Regarding answer R Moyer, the solution is excellent, but the bounds are not always installed after the cellForRowAt method, so as a possible refinement of his solution, it is to transfer the call of the setupShadow() method to the LayoutSubview() for example:

    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()
        }
    }
    

    screenshot

    0 讨论(0)
  • 2020-12-02 05:19

    This question comes at a good time! I literally JUST solved this same issue myself.

    1. Create a UIView (let's refer to it as mainBackground) inside your cell's Content View. This will contain all of your cell's content. Position it and apply necessary constraints in the Storyboard.
    2. Create another UIView. This one will be the one with the shadow (let's refer to it as shadowLayer). Position it exactly as you did mainBackground, but behind it, and apply the same constraints.
    3. Now you should be able to set the rounded corners and the shadows as follows:

      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
      

    However, the problem here is: calculating the shadow for every single cell is a slow task. You'll notice some serious lag when you scroll through your table. The best way to fix this is to define a UIBezierPath for the shadow, then rasterize it. So you may want to do this:

    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
    

    But this creates a new problem! The shape of the UIBezierPath depends on shadowLayer's bounds, but the bounds are not properly set by the time cellForRowAtIndexPath is called. So, you need to adjust the shadowPath based on shadowLayer's bounds. The best way to do this is to subclass UIView, and add a property observer to the bounds property. Then set all the properties for the shadow in didSet. Remember to change the class of your shadowLayer in the storyboard to match your new subclass.

    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
        }
    }
    
    0 讨论(0)
  • 2020-12-02 05:21

    I have achieved the same thing using following code.But you have place it in layoutSubviews() method of your TableViewCell subclass.

    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;
    
    0 讨论(0)
提交回复
热议问题