【问题标题】:Not able to remove custom UIView from SuperView无法从 SuperView 中删除自定义 UIView
【发布时间】:2019-08-09 20:20:58
【问题描述】:

这非常奇怪。当我向左或向右拖动视图时,我试图从超级视图中删除视图。如果视图不包含任何子视图,那么我可以使用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 上添加文本、标签和背景颜色。卡片是这样的:

我还在它上面使用了 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)
}

主视图控制器代码:

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
}


}

【问题讨论】:

  • 你在哪里添加卡片?你在使用 UICollectionView 吗?如果是这样,您不能随便删除 collectionview 项目。
  • @cekisakurek 我将这些卡片添加到单独的容器视图中。它基本上是一个 UIView,首先在屏幕中间加载。所有这些卡片都添加为 containerview 的子视图。
  • 请分享容器代码。
  • @cekisakurek 刚刚添加了containerView代码。
  • @RomanTsymbaliuk 添加了视图控制器代码,在数据源一致性方法中,您可以准确地看到我是如何从数据数组创建卡片并返回它的!

标签: ios swift uiview subview tinder


【解决方案1】:

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

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

只需删除此代码。在每个布局中,您设置 datasource... 和 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 的布局找到更好的解决方案

【讨论】:

  • 非常感谢!得到它的工作。使用 loadViews 函数加载所有视图,然后在 viewDidLoad 中调用 stackContainer.datasource = self。非常感谢!
  • 不客气!我还添加了我的 viewDidLoad 版本。
猜你喜欢
  • 2019-04-08
  • 1970-01-01
  • 2020-04-28
  • 2021-12-31
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多