无法从 SuperView 中删除自定义 UIView

Not able to remove custom UIView from SuperView

这非常奇怪。当我向左或向右拖动视图时,我试图从超级视图中删除视图。如果视图不包含任何子视图,那么我可以很容易地使用这个 card.removeFromSuperview() 从 superView 中删除视图 - 但是,我注意到,如果在卡片视图中添加两个视图作为子视图,那么我我无法将它从 superView 中删除,整个事情变得异常。

这是卡片视图 class:

class MainSwipeCardView: UIView {
//MARK: - Properties
var swipeView = UIView()
var shadowView = UIView()

var text: String?
var label = UILabel()
var bgColor : UIColor? {
    didSet {
        swipeView.backgroundColor = bgColor
    }
}


var cardsarraydata : CardDataModel? {
    didSet {
        bgColor = cardsarraydata?.backgroundColor
        label.text = cardsarraydata?.title
    }
}

var delegate : CardDelegate?

//MARK :- Init
override init(frame: CGRect) {
    super.init(frame: .zero)
    backgroundColor = .clear

    configureShadowView()
    configureSwipeView()
    configureLabelView()
    addPanGestureOnCards() 

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

//MARK: - Configurations
func configureShadowView() {
    shadowView.backgroundColor = .clear
    shadowView.layer.shadowColor = UIColor.black.cgColor
    shadowView.layer.shadowOffset = CGSize(width: 0, height: 0)
    shadowView.layer.shadowOpacity = 0.8
    shadowView.layer.shadowRadius = 4.0
    addSubview(shadowView)

    shadowView.translatesAutoresizingMaskIntoConstraints = false
    shadowView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    shadowView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    shadowView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    shadowView.topAnchor.constraint(equalTo: topAnchor).isActive = true
}

func configureSwipeView() {
    swipeView.layer.cornerRadius = 15
    swipeView.clipsToBounds = true
    shadowView.addSubview(swipeView)

    swipeView.translatesAutoresizingMaskIntoConstraints = false
    swipeView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    swipeView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    swipeView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    swipeView.topAnchor.constraint(equalTo: topAnchor).isActive = true
}

func configureLabelView() {
    swipeView.addSubview(label)
    label.backgroundColor = .white
    label.textColor = .black
    label.textAlignment = .center
    label.font = UIFont.systemFont(ofSize: 18)
    label.translatesAutoresizingMaskIntoConstraints = false
    label.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    label.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    label.heightAnchor.constraint(equalToConstant: 85).isActive = true

}

func addPanGestureOnCards() {
    self.isUserInteractionEnabled = true
    addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture)))
}

//MARK: - Handlers
@objc func handlePanGesture(sender: UIPanGestureRecognizer){
    let card = sender.view as! MainSwipeCardView
    let point = sender.translation(in: self)
    let centerOfParentContainer = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
    card.center = CGPoint(x: centerOfParentContainer.x + point.x, y: centerOfParentContainer.y + point.y)

    switch sender.state {
    case .ended:
        if (card.center.x) > 400 {
            delegate?.swipeDidEnd(on: card)

            UIView.animate(withDuration: 0.2) {
                card.center = CGPoint(x: centerOfParentContainer.x + point.x + 200, y: centerOfParentContainer.y + point.y + 75)
                card.alpha = 0
                self.layoutIfNeeded()
            }
            return
        }else if card.center.x < -115 {
            delegate?.swipeDidEnd(on: card)

            UIView.animate(withDuration: 0.2) {
                card.center = CGPoint(x: centerOfParentContainer.x + point.x - 200, y: centerOfParentContainer.y + point.y + 75)
                card.alpha = 0
                self.layoutIfNeeded()
            }
            return
        }
        UIView.animate(withDuration: 0.2) {
            card.transform = .identity
            card.center = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
            self.layoutIfNeeded()
        }

    default:
        break
    }

}

在这个子class 中,我有两个 UIView,我按顺序添加视图。然后在 swipeView 上添加文本和标签以及背景颜色。这是卡片的样子:

我还在其上使用了 UIPanInteraction,因此如果我将其向左或向右拖动,那么我将调用从容器视图中删除整个 MainSwipeCardView 的委托。这样做会发生以下情况:

即使这是我在委托函数中调用的内容,它仍会在后台添加越来越多的内容:

 func swipeDidEnd(on card: MainSwipeCardView) {
    card.removeFromSuperview()
    print(visibleCards.count)
}

visibleCards本质上是容器视图中的子视图数组。它应该减少,例如从 3 -> 2 -> 1;但它以非线性方式增加(无法真正从中得到关系)

最令人困惑的是,如果我不在自定义视图中添加 SwipeView 和 shadowView 属性,而只是使用 customView 本身来放置标签和背景颜色。当我添加这两个属性时,整个事情似乎变得一团糟。

如有任何帮助,我们将不胜感激。谢谢!

ContainerView代码如下:

class SwipeCardContainerView: UIView, CardDelegate {

//MARK: - Properties
var numberOfCards: Int = 0
var remainingCards: Int = 0
var cardsView : [MainSwipeCardView] = []
var numberOfAllowedCard: Int = 3

let horizontalInset: CGFloat = 8.0
let verticalInset: CGFloat = 8.0

var visibleCards : [MainSwipeCardView] {
    return subviews as? [MainSwipeCardView] ?? []
}

var datasource : CardDataSource? {
    didSet {
        loadData()
    }
}
override init(frame: CGRect) {
    super.init(frame: .zero)
    backgroundColor = .clear

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


// MARK: - Configuration
func loadData() {
    guard let datasource = datasource else { return }
    numberOfCards = datasource.numberOfCards()
    remainingCards = numberOfCards

    for i in 0..<min(numberOfCards,numberOfAllowedCard) {
           addCardView(at: i, card: datasource.createCard(at: i))
    }
 setNeedsLayout()

}

func addCardView(at index: Int, card: MainSwipeCardView) {
    card.delegate = self
    addCardFrame(index: index, cardView: card)
    cardsView.append(card)
    insertSubview(card, at: 0)
    remainingCards -= 1
}

func addCardFrame(index: Int, cardView: MainSwipeCardView){
    cardsView.append(cardView)

    var cardViewFrame = bounds
    let horizontalInset = (CGFloat(index) * self.horizontalInset)
    let verticalInset = CGFloat(index) * self.verticalInset

    cardViewFrame.size.width -= 2 * horizontalInset
    cardViewFrame.origin.x += horizontalInset
    cardViewFrame.origin.y += verticalInset
    cardView.frame = cardViewFrame
}

// Delegate Method
func swipeDidEnd(on card: MainSwipeCardView) {
    card.removeFromSuperview()
    print(visibleCards.count)
}

主要ViewController代码:

class ViewController: UIViewController {

//MARK: - Properties
var stackContainer : SwipeCardContainerView!
var cardDataArray : [CardDataModel] = [CardDataModel(backgroundColor: .orange, title: "Hello"),
                                       CardDataModel(backgroundColor: .red, title: "Red"),
                                       CardDataModel(backgroundColor: .blue, title: "Blue"),
                                       CardDataModel(backgroundColor: .orange, title: "Orange")]

//MARK: - Init
override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor(red:0.93, green:0.93, blue:0.93, alpha:1.0)
    stackContainer = SwipeCardContainerView()
    view.addSubview(stackContainer)
    configureSwipeContainerView()
    stackContainer.translatesAutoresizingMaskIntoConstraints = false
}

//MARK : - Configurations
func configureSwipeContainerView() {
      stackContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    stackContainer.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
      stackContainer.widthAnchor.constraint(equalToConstant: 300).isActive = true
      stackContainer.heightAnchor.constraint(equalToConstant: 350).isActive = true
}

override func viewDidLayoutSubviews() {
    stackContainer.datasource = self
}



//MARK : - Handlers


}

extension ViewController : CardDataSource {
func numberOfCards() -> Int {
    return cardDataArray.count
}

func createCard(at index: Int) -> MainSwipeCardView {
    let card = MainSwipeCardView()
    card.cardsarraydata = cardDataArray[index]
    return card
}

func emptyCard() -> UIView? {
    return nil
}


}

我已经调查了这个问题。 第一期在 ViewController:

    override func viewDidLayoutSubviews() {
        stackContainer.datasource = self
    }

只需删除此代码。在每个布局中,您设置数据源...和 ​​loadData...这是不正确的方法,还缺少 super.viewDidLayoutSubviews()...

还有stackContainer.datasource = self:

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor(red:0.93, green:0.93, blue:0.93, alpha:1.0)
        stackContainer = SwipeCardContainerView()
        view.addSubview(stackContainer)
        configureSwipeContainerView()
        stackContainer.translatesAutoresizingMaskIntoConstraints = false
        stackContainer.datasource = self

第二期在func loadData(),直接换成

    func loadData() {
        guard let datasource = datasource else { return }
        setNeedsLayout()
        layoutIfNeeded()
        numberOfCards = datasource.numberOfCards()
        remainingCards = numberOfCards

        for i in 0..<min(numberOfCards,numberOfAllowedCard) {
            addCardView(at: i, card: datasource.createCard(at: i))

        }
    }

或者通过 SwipeCardContainerView 的布局找到更好的解决方案