如何在以前在iOS 10上运行的iOS 11上的单元格解雇期间重新获得清晰的颜色UITableViewCell蒙版效果?

时间:2017-10-27 23:49:25

标签: swift uitableview ios10 calayer ios11

前言

我在iOS 10上有一个有效的自定义const recur = (...values) => ({ type: recur, values }) const loop = f => { let acc = f () while (acc && acc.type === recur) acc = f (...acc.values) return acc } const explore = $node => loop (([x,...xs] = [$node], k = x => x) => { if (x === undefined) return k () else { x.visited = true preVisit (x) return recur ( Array.from (x.adjacencies) .filter (n => n.visited === false) .concat (xs), () => (postVisit (x), k ()) ) } }) // for completion's sake let visitCounter = 0 const preVisit = node => node.preVisit = visitCounter++ const postVisit = node => node.postVisit = visitCounter++ 解雇动画。它的实现方式如下:

  1. 用户按下提交按钮
  2. 自定义视图在UITableViewCell的子视图的索引零处创建,定位和插入(在单元格的UITableViewCell下方)
  3. 自定义视图的alpha动画为1.0
  4. 时,contentView动画显示为帧外动画
  5. contentView完全超出框架时,会调用原生contentView方法UITableViewdeleteRows(at: [indexPath], with: .top)折叠,自定义视图在前一个UITableViewCell折叠后被屏蔽。
  6. 删除单元格(以及所有子视图,包括我的自定义视图)。
  7. 下面是它的慢动画:

    UITableViewCell mask working on iOS 10

    注意UITableViewCell具有清晰的背景色,允许在其后面显示自定义视图(蓝色)。每个单元格都有tableView,其中包含单元格的所有内容。 containerViewcontainerView都有明确的背景颜色。一切都很好,花花公子。

    问题

    将我的应用迁移到iOS 11,此动画无法正常运行。下面是一个慢动画,它不再有效了。

    UITableViewCell mask working on iOS 11

    正如您所看到的,自定义视图在单元格解除后覆盖在上一个单元格的,而不会更改我的代码。

    到目前为止的调查

    到目前为止,我已经确定contentView的解剖结构已经改变了:

    UITableView

    对此:( UITableView UITableViewWrapperView cell custom view contentView cell separator view cell cell cell UIView UIImageView (scroll view indicator bottom) UIImageView (scroll view indicator right) 已被删除)

    UITableViewWrapperView

    我注意到有关此UITableView cell custom view contentView cell separator view cell cell cell UIView UIImageView (scroll view indicator bottom) UIImageView (scroll view indicator right) 的一件事是它的图层的UITableWrapperView属性为true,而isOpaque属性为false masksToBounds和所有UITableView相反。由于此视图在iOS 11中被删除,这可能会导致我出现错误效果的原因。我真的不知道。

    编辑:我发现的另一件事是工作示例中的UITableViewCell在其子视图的零索引处插入了一个神秘的UITableWrapperView(所有UIView } s)哪些属性UITableViewCell设置为true并且它具有isOpaque。随后在动画完成后删除此视图。由于在iOS 11中删除了compositingFilter,因此该视图通过关联也会丢失。

    问题

    首先,是否有人知道为什么这种行为发生了变化?如果没有,是否有另一种方法可以更好地实现iOS 10中的效果?我希望清楚UITableWrapperView s但是有一个自定义视图,在解雇时显示在每个单元格后面,当被解除时被其他清除UITableViewCell屏蔽,如上面第一个gif示例所示。

5 个答案:

答案 0 :(得分:1)

在删除行之前,请将要删除的单元格发送到表格视图子视图的背面。

tableView.sendSubview(toBack: cell)
tableView.deleteRows(at: [indexPath], with: .top)

如果您没有单元格,可以使用索引路径检索它。

guard let cell = tableView.cell(for: indexPath) else { return }

// ...

这应该适用于视图层次结构的调整。

View hierarchy adjustment

<强> CardTableViewController.swift

class CardTableViewController: UITableViewController, CardTableViewCellDelegate {

    // MARK: Outlets

    @IBOutlet var segmentedControl: UISegmentedControl!

    // MARK: Properties

    private var cards: [Card] = [
        Card(text: "Etiam porta sem malesuada magna mollis euismod. Maecenas faucibus mollis interdum."),
        Card(text: "Nulla vitae elit libero, a pharetra augue. Cras justo odio, dapibus ac facilisis in, egestas eget quam."),
        Card(text: "Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Sed posuere consectetur est at lobortis.")
    ]

    var selectedIndex: Int { return segmentedControl.selectedSegmentIndex }
    var tableCards: [Card] {
        return cards.filter { selectedIndex == 0 ? !$0.isDone : $0.isDone }
    }

    // MARK: UITableViewDataSource

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return tableCards.count
    }

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

        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CardTableViewCell

        cell.card = tableCards[indexPath.row]

        return cell
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

        return UITableViewAutomaticDimension
    }

    // MARK: CardTableViewCellDelegate

    func cardTableViewCell(_ cell: CardTableViewCell, didTouchUpInsideCheckmarkButton button: UIButton) {

        guard let indexPath = tableView.indexPath(for: cell) else { return }

        cell.card?.isDone = button.isSelected
        cell.card?.dayCount += button.isSelected ? 1 : -1
        cell.customView?.dayCount = cell.card?.dayCount

        UIView.animate(withDuration: 0.7, delay: 0.3, options: .curveEaseIn, animations: {
            cell.checkmarkButton?.superview?.transform = .init(translationX: cell.frame.width, y: 0)
            cell.customView?.alpha = 1
        }) { _ in
            self.tableView.sendSubview(toBack: cell)
            self.tableView.deleteRows(at: [indexPath], with: .top)
        }
    }

    // MARK: Actions

    @IBAction func didChangeSegmentedControl(_ sender: UISegmentedControl) {

        tableView.reloadData()
    }
}

<强> CardTableViewCell.swift

@objc protocol CardTableViewCellDelegate {
    func cardTableViewCell(_ cell: CardTableViewCell, didTouchUpInsideCheckmarkButton button: UIButton)
}

@IBDesignable class CardTableViewCell: UITableViewCell {

    // MARK: Outlets

    @IBOutlet var checkmarkButton: UIButton?
    @IBOutlet var customView: CardCustomView?
    @IBOutlet var delegate: CardTableViewCellDelegate?
    @IBOutlet var taskTextLabel: UILabel?

    // MARK: Properties

    var card: Card? {
        didSet {
            updateOutlets()
        }
    }

    // MARK: Lifecycle

    override func awakeFromNib() {

        super.awakeFromNib()

        checkmarkButton?.layer.borderColor = UIColor.black.cgColor
    }

    override func prepareForReuse() {

        super.prepareForReuse()

        card = nil
    }

    // MARK: Methods

    private func updateOutlets() {

        checkmarkButton?.isSelected = card?.isDone == true ? true : false
        checkmarkButton?.superview?.transform = .identity

        customView?.alpha = 0
        customView?.dayCount = card?.dayCount

        taskTextLabel?.text = card?.text
    }

    // MARK: Actions

    @IBAction func didTouchUpInsideCheckButton(_ sender: UIButton) {

        sender.isSelected = !sender.isSelected

        delegate?.cardTableViewCell(self, didTouchUpInsideCheckmarkButton: sender)
    }
}

class CardCustomView: UIView {

    // MARK: Outlets

    @IBOutlet var countLabel: UILabel?
    @IBOutlet var daysLabel: UILabel?

    // MARK: Properties

    var dayCount: Int? {
        didSet { 
            updateOutlets()
        }
    }

    override func awakeFromNib() {

        super.awakeFromNib()

        updateOutlets()
    }

    // MARK: Methods

    private func updateOutlets() {

        let count = dayCount.flatMap { $0 } ?? 0

        countLabel?.text = "\(dayCount.flatMap { $0 } ?? 0)"
        daysLabel?.text = "Day\(count == 1 ? "" : "s") in a row"
    }
}

<强> Card.swift

class Card: NSObject {

    // MARK: Properties

    var dayCount: Int = 0
    var isDone: Bool = false
    var text: String

    // MARK: Lifecycle

    init(text: String) {
        self.text = text
    }
}

答案 1 :(得分:1)

首先,我不认为缺少UITableViewWrapperView是个问题。 这是父视图,父母不能掩盖儿童观点。

请注意,问题是哪个单元格遮挡了哪个。在第一种情况下,行A遮挡了行B,而在第二行B遮蔽了行A(在这种情况下,您的自定义B单元格具有透明背景)。

看起来苹果已经搞砸了细胞之间的关系。为了解决这个问题,我会尝试修复这种关系。所以在动画的第二部分开始之前我会尝试:

bring cell to back

cell.superview.sendSubview(toBack: cell)

这不应该破坏任何东西(在较旧的iOS版本上)并且应该解决iOS 11的问题。

答案 2 :(得分:1)

我花了很多天学习但却失败了。

<强> iOS10

deleteRows之后,TableViewCell的视图中包含compositingFilter属性,其值为copy,视图隐藏了此背后的所有视图。

我们可以尝试删除TableViewCell时生成的deleteRows子视图。然后iOS 10的行为就像iOS11。

然后我们可以尝试再次将视图(UIView())添加到TableViewCell并将compositingFilter设置为copy(字符串),它将重新实现in iOS10试。

但iOS11中的任何操作失败。它将在iOS11中产生黑色背景。

我相信在iOS11上更改了图层的行为。

up的背景透明时,我们不得不考虑暂时放弃使用ios11中的TableViewCell动画。

答案 3 :(得分:1)

在iOS 11上查看应用程序后我遇到了这样的问题,问题是从表格视图中删除行时的动画:

enter image description here

很明显,折叠动画是不合适的,因为要删除多行。

以下是扩展/折叠行的负责代码:

// inserting rows:                
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: 4 + count, section: 0), IndexPath(row: 5 + count, section: 0), IndexPath(row: 6 + count, section: 0)], with: .automatic)
tableView.endUpdates()

// deleting rows:
tableView.beginUpdates()
tableView.deleteRows(at: [IndexPath(row: 4 + count, section: 0), IndexPath(row: 5 + count, section: 0), IndexPath(row: 6 + count, section: 0)], with: .automatic)
tableView.endUpdates()

显然,还有编辑数据源数组的代码。


导致此问题的原因是 - 只是 - 已分配的UITableViewRowAnimationautomatic;我所做的只是将动画更改为fade

// inserting rows:                
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: 4 + count, section: 0), IndexPath(row: 5 + count, section: 0), IndexPath(row: 6 + count, section: 0)], with: .fade)
tableView.endUpdates()

// deleting rows:
tableView.beginUpdates()
tableView.deleteRows(at: [IndexPath(row: 4 + count, section: 0), IndexPath(row: 5 + count, section: 0), IndexPath(row: 6 + count, section: 0)], with: .fade)
tableView.endUpdates()

输出已修复为:

enter image description here

同时

您可能需要查看可能与您的问题相关的以下问题:

答案 4 :(得分:0)

我找到了UITableView具有固定大小的单元格

的解决方案

presentation of the solution in action

在这种方法中,我决定在UITableViewCell的内容视图中添加补充视图,并将其高度约束设置为0以及行删除动画。

为了简单起见,我只介绍此解决方案的要点。

  • 用户点击删除按钮。
  • Cell将内部内容视图移动到右侧。
  • 表视图调用deleteRows(at:with:)方法。
  • Cell将补充视图的高度设置为0

我不会在这里描述整个自动布局设置,因为它会模糊解决方案的主要思想。所有构建块都相当简单,但有一些。您可以在此GitHub repository中找到完整的工作项目。

不幸的是,由于动画定时,这种解决方案对动态高度单元的效果不佳。我不确定它会解决您的问题,因为据我所知,您正在使用动态高度单元格。 但是,我希望它会推动你朝着正确的方向前进,或者帮助有类似问题的人。