Не удалось удалить пользовательский UIView из SuperView - PullRequest
0 голосов
/ 19 марта 2019

Это очень странно. Я пытаюсь удалить вид из суперпредставления, когда перетаскиваю вид влево или вправо. Если представление не содержит никаких подпредставлений, тогда я легко могу удалить представление из superView, используя этот 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
    }

}

В этом подклассе у меня есть два UIViews, я последовательно добавляю представления. Затем на 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)
}

Основной код контроллера:

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 Ответ

1 голос
/ 19 марта 2019

Я исследовал проблему. Первая проблема в 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

...