【问题标题】:Constraining UITableViewCell to right edge of UITableView将 UITableViewCell 约束到 UITableView 的右边缘
【发布时间】:2020-09-07 05:17:52
【问题描述】:

由于某种原因,当我尝试使用以下方法在单元格上放置阴影时,它并没有达到单元格的整个宽度:

cell.layer.shadowPath = UIBezierPath(roundedRect:cell.bounds, cornerRadius:cell.contentView.layer.cornerRadius).cgPath

有谁知道如何解决这个问题?

当然,我的最终目的是在左右两侧留出间距,但我觉得第一步是让它到达边缘。

模拟器和故事板:

代码:

import UIKit

class SpendingCategoryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    //MARK: Properties

    @IBOutlet weak var spendingCategoriesTableView: UITableView!
    var spendingCategories = [SpendingCategory]()

    override func viewDidLoad() {
        super.viewDidLoad()
        spendingCategoriesTableView.delegate = self
        spendingCategoriesTableView.dataSource = self

        // Load the sample data.
        loadSampleCategories()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return spendingCategories.count
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        // Table view cells are reused and should be dequeued using a cell identifier.
        let cellIdentifier = "SpendingCategoryTableViewCell"

        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? SpendingCategoryTableViewCell  else {
            fatalError("The dequeued cell is not an instance of SpendingCategoryTableViewCell.")
        }

        // Fetches the appropriate SpendingCategory for the data source layout.
        let spendingCategory = spendingCategories[indexPath.row]

        cell.categoryLabel.text = spendingCategory.name
        cell.iconImageView.image = spendingCategory.icon
        cell.valueLabel.text = String(format: "$%.02f", spendingCategory.total)

        // This creates the shadows and modifies the cards a little bit
        // https://github.com/rileydnorris/cardLayoutSwift/blob/9b852fc8e1b7d62093be787a33a3a89d764dc9b8/cardLayout/ViewController.swift

        cell.contentView.layer.cornerRadius = 2.0
//        cell.contentView.layer.borderWidth = 1.0
        cell.contentView.layer.borderColor = UIColor.clear.cgColor
        cell.contentView.layer.masksToBounds = true;

        cell.layer.shadowColor = UIColor.lightGray.cgColor
        cell.layer.shadowOffset = CGSize(width:0,height: 2.0)
        cell.layer.shadowRadius = 2.0
        cell.layer.shadowOpacity = 1.0
        cell.layer.masksToBounds = false;
        cell.layer.shadowPath = UIBezierPath(roundedRect:cell.bounds, cornerRadius:cell.contentView.layer.cornerRadius).cgPath




        return cell
    }


    /*
     // Override to support conditional editing of the table view.
     override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
     // Return false if you do not want the specified item to be editable.
     return true
     }
     */

    /*
     // Override to support editing the table view.
     override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
     if editingStyle == .delete {
     // Delete the row from the data source
     tableView.deleteRows(at: [indexPath], with: .fade)
     } else if editingStyle == .insert {
     // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
     }
     }
     */

    /*
     // Override to support rearranging the table view.
     override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

     }
     */

    /*
     // Override to support conditional rearranging of the table view.
     override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
     // Return false if you do not want the item to be re-orderable.
     return true
     }
     */

    /*
     // MARK: - Navigation

     // In a storyboard-based application, you will often want to do a little preparation before navigation
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
     // Get the new view controller using segue.destinationViewController.
     // Pass the selected object to the new view controller.
     }
     */

    //MARK: Private Methods
    @IBAction func addCategoryButtonPress(_ sender: UIButton) {
        // Create an alert
        let alert = UIAlertController(
            title: "Add a new category",
            message: "",
            preferredStyle: .alert)

        // Add a text field to the alert for the new item's title
        alert.addTextField(configurationHandler: nil)

        // Add a "cancel" button to the alert. This one doesn't need a handler
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

        // Add a "OK" button to the alert. The handler calls addNewCategory()
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler:
            { (_) in
                // Get the title the user inserted, but only if it is not an empty string
                if let title = alert.textFields?[0].text, title.count > 0
                {
                    print("took in alert input")
                    self.addNewCategory(title: title)
                    //                    self.containerViewController?.viewDidLoad()
                }
        }))

        // Present the alert to the user
        self.present(alert, animated: true, completion: nil)
    }

    internal func addNewCategory(title: String)
    {
        // The index of the new item will be the current item count
        print("reached addNewCategory")
        let newIndex = spendingCategories.count

        // Create new item and add it to the todo items list
        spendingCategories.append(SpendingCategory(name: title, icon: UIImage(named: "Default")!, total: 0)!)

        // Tell the table view a new row has been created
        self.spendingCategoriesTableView.insertRows(at: [IndexPath(row: newIndex, section: 0)], with: .bottom)
        print(spendingCategories)
    }

    private func loadSampleCategories() {
        let photo1 = UIImage(named: "Default")

        guard let category1 = SpendingCategory(name: "Groceries", icon: photo1!, total: 4) else {
            fatalError("Unable to instantiate category1")
        }

        guard let category2 = SpendingCategory(name: "Transportation", icon: photo1!, total: 12345678) else {
            fatalError("Unable to instantiate category2")
        }

        guard let category3 = SpendingCategory(name: "Alcohol something really long", icon: photo1!, total: 12345) else {
            fatalError("Unable to instantiate category3")
        }

        spendingCategories += [category1, category2, category3]
    }
}

import UIKit

class SpendingCategoryTableViewCell: UITableViewCell {

    //MARK: Properties
    @IBOutlet weak var categoryLabel: UILabel!
    @IBOutlet weak var iconImageView: UIImageView!
    @IBOutlet weak var valueLabel: UILabel!


    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

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

        // Configure the view for the selected state
    }

}

【问题讨论】:

  • 请也发布您的自定义单元格SpendingCategoryTableViewCell 课程
  • @jawadAli 当然,已编辑!
  • 向单元格添加了哪些约束?我看到的是尾随约束问题
  • @jawadAli 我的最终目标是添加约束,以在单元格周围提供对称缓冲区(比如大约 10 像素)
  • 但首先你想要全屏单元格对吗?带有全宽阴影...当前尾随截断但未领先

标签: ios swift uitableview storyboard


【解决方案1】:

你可以更容易地做到这一点......

添加 UIView 并将其限制在单元格的内容视图中 - 使用其默认边距或设置您自己的边距。

将您的图像视图和标签添加到该视图。

在您的单元格类中,根据需要设置角半径、阴影、阴影半径等:

class SpendingCategoryTableViewCell: UITableViewCell {

    @IBOutlet var shadowedView: UIView!
    @IBOutlet var theImageView: UIImageView!
    @IBOutlet var catLabel: UILabel!
    @IBOutlet var moneyLabel: UILabel!

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    override func awakeFromNib() {
        super.awakeFromNib()
        commonInit()
    }
    func commonInit() -> Void {
        guard let cv = shadowedView else {
            return
        }
        cv.backgroundColor = .white
        cv.layer.cornerRadius = 0.0
        cv.layer.shadowColor = UIColor.black.cgColor
        cv.layer.shadowOffset = CGSize(width: 0, height: 2)
        cv.layer.shadowRadius = 2.0
        cv.layer.shadowOpacity = 0.5
    }

}

结果:

还有,cv.layer.cornerRadius = 8.0 的示例:

【讨论】:

    【解决方案2】:

    在我的项目中,我最终将自己的背景视图添加到单元格contentView。忘记直接自定义contentView。然后我在我的背景视图中应用了拐角 - awakeFromNib() 中的 actualCellView 并在情节提要中为单元格边缘添加了约束(您也可以以编程方式在 layoutSubview() 中设置它们。

    警告:

    阴影 + 角落在 iOS 中是一个巨大的痛苦。

    如果您想在视图中添加角AND 阴影,您应该使用附加视图作为角/阴影或在draw(_ rect: CGRect) 中添加附加CALayer。所以我最终采用了第一种方法。还最好将您的视图特定代码移动到单独的单元格类。 例如,查看我的 CommentCell 课程:

    import UIKit
    
    class CommentCell: UITableViewCell {
    
        @IBOutlet weak var authorLabel: UILabel!
        @IBOutlet weak var commentLabel: UILabel!
        @IBOutlet weak var actualCellView: UIView!
    
        var cornerView: UIView = UIView() // visible corner view
        var preferredCornerRadius: CGFloat = 10
    
        override func awakeFromNib() {
            super.awakeFromNib()
            setup()
        }
    
        override func setup() {
            super.setup()
    
            addCorners(radius: preferredCornerRadius, to: actualCellView, cornerView: cornerView)
            dropActualShadow()
        }
    
        override func dropActualShadow() {
            actualCellView.dropShadow(color: .gray,
                                      opacity: 0.25,
                                      offSet: CGSize(width: 0, height: 2),
                                      blurRadius: 1, scale: true,
                                      rounded: true,
                                      cornerRadius: preferredCornerRadius)
        }
    
        override func draw(_ rect: CGRect) {
            super.draw(rect)
            dropActualShadow()
        }
    
    }
    

    为视图启用角和阴影的扩展:

    public extension UIView {    
        // Careful with this method - adding multiple cornered subviews is not checked 
        func addCorners(radius: CGFloat = 10, to subviewView: UIView, with backgroundColor: UIColor = .white, cornerView: UIView = UIView()) {
    
            subviewView.insertSubview(cornerView, at: 0)
    
            cornerView.backgroundColor = backgroundColor
            subviewView.backgroundColor = .clear
    
            cornerView.cornerRadius = radius
            cornerView.layer.masksToBounds = true
    
            cornerView.translatesAutoresizingMaskIntoConstraints = false
            cornerView.leadingAnchor.constraint(equalTo: subviewView.leadingAnchor).isActive = true
            cornerView.trailingAnchor.constraint(equalTo: subviewView.trailingAnchor).isActive = true
            cornerView.topAnchor.constraint(equalTo: subviewView.topAnchor).isActive = true
            cornerView.bottomAnchor.constraint(equalTo: subviewView.bottomAnchor).isActive = true
        }
    
        func dropShadow(color: UIColor = .black,
                        opacity: Float = 0.5,
                        offSet: CGSize = .zero,
                        blurRadius: CGFloat = 1,
                        scale: Bool = true,
                        rounded: Bool = false,
                        dx: CGFloat = 0,
                        dy: CGFloat = 0,
                        cornerRadius: CGFloat = 10,
                        roundCorners: UIRectCorner = .allCorners) {
    
            self.layer.masksToBounds = false
            self.layer.shadowColor = color.cgColor
            self.layer.shadowOpacity = opacity
            self.layer.shadowOffset = offSet
            self.layer.shadowRadius = blurRadius
            let finalRect = self.bounds.insetBy(dx: dx, dy: dy)
            self.layer.shadowPath = rounded ?
                UIBezierPath(roundedRect: finalRect,
                             byRoundingCorners: roundCorners,
                             cornerRadii: CGSize(width: cornerRadius,
                                                 height: cornerRadius)).cgPath :
                UIBezierPath(rect: finalRect).cgPath
            self.layer.shouldRasterize = true
            self.layer.rasterizationScale = scale ? UIScreen.main.scale : 1
        }
    }
    

    希望这可以为您节省几天时间。

    【讨论】:

    • 谢谢,我会试试这个并告诉你!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    • 2017-05-15
    • 2018-12-29
    • 1970-01-01
    • 2022-12-19
    • 1970-01-01
    相关资源
    最近更新 更多