无法从SuperView中删除自定义UIView

时间:2019-03-19 10:27:24

标签: ios swift uiview subview tinder

这很奇怪。当我向左或向右拖动视图时,我试图从超级视图中删除视图。如果该视图不包含任何子视图,那么我可以轻松地通过使用此card.removeFromSuperview()从超级视图中删除该视图-但是,我注意到的是,如果将两个视图作为子视图添加到卡片视图中,那么我无法将其从superView中删除,整个过程就变成了疯子。

这是卡片视图类:

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
    }

}

在此子类中,我有两个UIView,我将按顺序添加视图。然后在swipeView上添加文本,标签和背景色。卡的外观如下: enter image description here

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

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

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

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

最令人困惑的是,如果我不在自定义视图内添加SwipeView和shadowView属性,而仅使用customView本身来容纳标签和backgroundColor,我实际上就能运行整个代码。当我添加这两个属性时,整个过程似乎很麻烦。

请提供任何帮助,我们将不胜感激。谢谢!

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
}


}

1 个答案:

答案 0 :(得分:1)

我已经调查了这个问题。 第一个问题是在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的布局找到更好的解决方案