自定义UIView添加子视图后无法从父视图移除问题求助
Hey there, let's break down why your MainSwipeCardView isn't removing properly once you add shadowView and swipeView, and fix it step by step:
1. Wrong Constraint Anchors Are Messing Up Layout
The first big issue is in your constraint setup for swipeView and label. When you add a subview to another view, its constraints should reference the parent subview, not the top-level MainSwipeCardView. Right now, you're anchoring swipeView (a child of shadowView) to self's anchors, which breaks the view hierarchy and can interfere with gesture recognition and view removal.
Fixed Code:
func configureSwipeView() { swipeView.layer.cornerRadius = 15 swipeView.clipsToBounds = true shadowView.addSubview(swipeView) swipeView.translatesAutoresizingMaskIntoConstraints = false // Anchor to shadowView instead of self swipeView.leftAnchor.constraint(equalTo: shadowView.leftAnchor).isActive = true swipeView.rightAnchor.constraint(equalTo: shadowView.rightAnchor).isActive = true swipeView.bottomAnchor.constraint(equalTo: shadowView.bottomAnchor).isActive = true swipeView.topAnchor.constraint(equalTo: shadowView.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 // Anchor to swipeView instead of self label.leftAnchor.constraint(equalTo: swipeView.leftAnchor).isActive = true label.rightAnchor.constraint(equalTo: swipeView.rightAnchor).isActive = true label.bottomAnchor.constraint(equalTo: swipeView.bottomAnchor).isActive = true label.heightAnchor.constraint(equalToConstant: 85).isActive = true }
2. Incorrect Parent Container Center Calculation
In your pan gesture handler, you're calculating the parent container's center using self.frame.width / 2—but self is the card itself, not its parent SwipeCardContainerView. This leads to wrong drag positioning, which can make the card appear to not move correctly and interfere with the removal trigger.
Fixed Gesture Handler:
@objc func handlePanGesture(sender: UIPanGestureRecognizer){ guard let card = sender.view as? MainSwipeCardView, let parentContainer = card.superview else { return } let translation = sender.translation(in: parentContainer) let parentCenter = CGPoint(x: parentContainer.frame.width / 2, y: parentContainer.frame.height / 2) card.center = CGPoint(x: parentCenter.x + translation.x, y: parentCenter.y + translation.y) switch sender.state { case .ended: let cardCenterX = card.center.x // Use parent container bounds to check if card is fully swiped out if cardCenterX > parentContainer.frame.width + 50 { delegate?.swipeDidEnd(on: card) UIView.animate(withDuration: 0.2) { card.center = CGPoint(x: parentContainer.frame.width + 200, y: parentCenter.y + translation.y + 75) card.alpha = 0 } completion: { _ in // Remove after animation completes to avoid layout glitches card.removeFromSuperview() } return } else if cardCenterX < -50 { delegate?.swipeDidEnd(on: card) UIView.animate(withDuration: 0.2) { card.center = CGPoint(x: -200, y: parentCenter.y + translation.y + 75) card.alpha = 0 } completion: { _ in card.removeFromSuperview() } return } // Reset card position if not swiped far enough UIView.animate(withDuration: 0.2) { card.transform = .identity card.center = parentCenter } default: break } }
3. Duplicate Card Additions to cardsView
In SwipeCardContainerView's addCardFrame method, you're calling cardsView.append(cardView) again—but this already happens in addCardView. This causes duplicate entries in your card array, leading to incorrect visibleCards counts and weird behavior when removing cards.
Fixed addCardFrame:
func addCardFrame(index: Int, cardView: MainSwipeCardView){ // Remove the duplicate append call here 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 }
4. Move removeFromSuperview to Animation Completion
Previously, you were removing the card immediately when the swipe ends, before the animation finishes. This can cause layout inconsistencies because the view is removed mid-animation. Moving the removal to the animation's completion closure ensures the view is only removed after it's visually gone.
After applying all these fixes, your swipe cards should remove correctly, and the visibleCards count will properly decrease from 3→2→1 as expected.
内容的提问来源于stack exchange,提问作者Osama Naeem

