是否可以从其父视图的边界中取消剪裁单个子视图?

Is it possible to unclip just a single subview from its superview's bounds?

我有这样一种情况,我希望单个子视图 被剪裁到父视图的边界,而父视图的其余子视图 剪到它的边界。有没有办法将某个 UIView 排除在其父视图的边界之外。类似地,UIView 可以指定它 does/doesn 不想将哪些子视图裁剪到它自己的边界吗?

就代码而言,我正在使用 Koloda 创建类似于 Tinder 刷卡的 UIView。我有一个集合视图,其单元格本质上是 Koloda 视图。每当卡片开始平移时,我将其单元格的集合视图的 clipsToBounds 属性 设置为 false。这允许单元格在导航栏上方平移而不被剪裁。当平移结束时,视图的单元格集合视图的 clipsToBounds 属性 设置回 true.

import UIKit
import Koloda

class LikedUserCollectionViewCell: UICollectionViewCell {
    @IBOutlet var swipeView: KolodaView!
    var parentVC: MyViewController!
}

extension LikedUserCollectionViewCell: KolodaViewDelegate, KolodaViewDataSource {
    func koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {
        //return a KolodaView, this code isn't important to the issue
        return KolodaView()
    }

    func kolodaNumberOfCards(_ koloda: KolodaView) -> Int {
        return 1
    }

    func kolodaPanBegan(_ koloda: KolodaView, card: DraggableCardView) {
        self.parentVC.collectionView.bringSubviewToFront(self)
        //Here, instead of unclipping all of the collection view's subviews, I would like to only unclip the one containing the subview that is being panned
        self.parentVC.collectionView.clipsToBounds = false
    }

    func kolodaPanFinished(_ koloda: KolodaView, card: DraggableCardView) {
        //Once the pan is finished, all of the collection view's subviews can be clipped again
        self.parentVC.collectionView.clipsToBounds = true
    }

    func koloda(_ koloda: KolodaView, didSwipeCardAt index: Int, in direction: SwipeResultDirection) {
        //Not relevant to the question
    }
}

问题是,当某些单元格已部分滚出集合视图,但由于它们尚未完全滚出集合视图而尚未从集合视图中移除时,它们现在变得可见,因为集合视图的 clipsToBounds 现在设置为 true。这就是为什么我希望能够指定将哪些集合视图的子视图裁剪到其边界。这种行为可以在下面的 gif 中看到:

所以,总而言之,有没有办法,而不是通过 superview.clipsToBounds = false 从它们的父视图的边界中取消剪裁所有子视图,我只能从它的父视图的边界中取消剪裁一个子视图?

这里的简单解决方案是,既然你想为单个视图做一个例外,那么不要移动collectionView单元格。使用 snapshotView and move the snapshot instead; it can be anywhere else in the hierarchy and does not need to be a child of the collection view. You can use UIView.convert(from:) 将单元格坐标转换为适当的父视图坐标 space 以正确放置快照。

从 OP 编辑​​:

我采纳了 Josh 的建议并将我的代码更新为以下内容,从而解决了我的问题:

import UIKit
import Koloda

class LikedUserCollectionViewCell: UICollectionViewCell {
    @IBOutlet var swipeView: KolodaView!
    var parentVC: MyViewController!
    //DraggableCardView is a custom Koloda class
    var draggableCardView: DraggableCardView!
}

extension LikedUserCollectionViewCell: KolodaViewDelegate, KolodaViewDataSource {
    func koloda(_ koloda: KolodaView, viewForCardAt index: Int) -> UIView {
        //return a KolodaView, this code isn't important to the issue
        return KolodaView()
    }

    func kolodaNumberOfCards(_ koloda: KolodaView) -> Int {
        return 1
    }

    func kolodaPanBegan(_ koloda: KolodaView, card: DraggableCardView) {
        let snapshotView = self.swipeView.snapshotView(afterScreenUpdates: false)!
        snapshotView.accessibilityIdentifier = "snapshotView"
        self.parentVC.view.addSubview(snapshotView)
        snapshotView.frame.origin = self.parentVC.view.convert(swipeView.viewForCard(at: 0)!.frame.origin, from: swipeView)
        draggableCardView = card
        swipeView.viewForCard(at: 0)?.isHidden = true
    }

    func koloda(_ koloda: KolodaView, draggedCardWithPercentage finishPercentage: CGFloat, in direction: SwipeResultDirection) {
        let snapshotView = self.parentVC.view.subviews.first(where: { [=10=].accessibilityIdentifier == "snapshotView" })!
        snapshotView.frame.origin = self.parentVC.view.convert(draggableCardView.frame.origin, from: swipeView)
    }

    func kolodaPanFinished(_ koloda: KolodaView, card: DraggableCardView) {
        let snapshotView = self.parentVC.view.subviews.first(where: { [=10=].accessibilityIdentifier == "snapshotView" })!
        draggableCardView = nil
        snapshotView.removeFromSuperview()
        swipeView.viewForCard(at: 0)?.isHidden = false
    }

    func koloda(_ koloda: KolodaView, didSwipeCardAt index: Int, in direction: SwipeResultDirection) {
        //Not relevant to the question
    }
}

看你的界面,collectionView为什么在(导航栏)上面?

在这种情况下您不需要任何裁剪,只要 collectionView 在导航栏后面即可。

调用 sendSubview(toBack: collectionView) 或将 collectionView.layer.zPosition 设置为更大的值。