Я пытаюсь реализовать программную версию динамического представления стека в Поваренная книга Apple Auto Layout. Кнопка «Добавить элемент» предназначена для добавления новых представлений в вертикальный stackView, включая кнопку удаления для удаления каждого представления. Мой программный код отлично работает для 1 нажатия кнопки «Добавить элемент», но затем эта кнопка становится неактивной. В результате я могу добавить только 1 элемент в stackView. Если я использовал кнопку удаления, «Добавить элемент» снова становится активным. Я включил анимированный gif для иллюстрации.

Я публикую как свой программный код (в котором есть проблема), так и ниже исходный код на основе раскадровки (который отлично работает). Я пробовал поставить точку останова отладки в функции addEntry, но это не помогло. -Спасибо

Программный код (кнопка «Добавить элемент» работает только один раз):

import UIKit

class CodeDynamStackVC: UIViewController {

  // MARK: Properties
  var scrollView = UIScrollView()
  var stackView = UIStackView()
  var button = UIButton()

  // MARK: UIViewController
  override func viewDidLoad() {
    super.viewDidLoad()
    // Set up the scrollview
    let insets = UIEdgeInsets(top: 20, left: 0.0, bottom: 0.0, right: 0.0)
    scrollView.contentInset = insets
    scrollView.scrollIndicatorInsets = insets
    setupInitialVertStackView()
  }
  //setup initial button inside vertical stackView
  func setupInitialVertStackView() {
    // make inital "Add Item" button
    button = UIButton(type: .system)
    button.setTitle("Add Item", for: .normal)
    button.setTitleColor(UIColor.blue, for: .normal)
    button.addTarget(self, action: #selector(addEntry), for: .touchUpInside)
    //enclose button in a vertical stackView
    stackView.addArrangedSubview(button)
    stackView.axis = .vertical
    stackView.alignment = .fill
    stackView.distribution = .equalSpacing
    stackView.spacing = 5
    stackView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(stackView)
    let viewsDictionary = ["v0":stackView]
    let stackView_H = NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary)
    let stackView_V = NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[v0(25)]|", options: NSLayoutFormatOptions(rawValue:0), metrics: nil, views: viewsDictionary)
    view.addConstraints(stackView_H)
    view.addConstraints(stackView_V)
  }

  // MARK: Interface Builder actions
  func addEntry() {
    guard let addButtonContainerView = stackView.arrangedSubviews.last else { fatalError("Expected at least one arranged view in the stack view.") }
    let nextEntryIndex = stackView.arrangedSubviews.count - 1
    let offset = CGPoint(x: scrollView.contentOffset.x, y: scrollView.contentOffset.y + addButtonContainerView.bounds.size.height)
    let newEntryView = createEntryView()
    newEntryView.isHidden = true
    stackView.insertArrangedSubview(newEntryView, at: nextEntryIndex)
    UIView.animate(withDuration: 0.25, animations: {
      newEntryView.isHidden = false
      self.scrollView.contentOffset = offset
    })
  }

  func deleteStackView(_ sender: UIButton) {
    guard let entryView = sender.superview else { return }
    UIView.animate(withDuration: 0.25, animations: {
      entryView.isHidden = true
    }, completion: { _ in
      entryView.removeFromSuperview()
    })
  }

  // MARK: Convenience

  /// Creates a horizontal stackView entry to place within the parent vertical stackView
  fileprivate func createEntryView() -> UIView {
    let date = DateFormatter.localizedString(from: Date(), dateStyle: .short, timeStyle: .none)
    let number = UUID().uuidString
    let stack = UIStackView()
    stack.axis = .horizontal
    stack.alignment = .center
    stack.distribution = .fill
    stack.spacing = 8

    let dateLabel = UILabel()
    dateLabel.text = date
    dateLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)

    let numberLabel = UILabel()
    numberLabel.text = number
    numberLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.caption2)
    numberLabel.setContentHuggingPriority(UILayoutPriorityDefaultLow - 1.0, for: .horizontal)
    numberLabel.setContentCompressionResistancePriority(UILayoutPriorityDefaultHigh - 1.0, for: .horizontal)

    let deleteButton = UIButton(type: .roundedRect)
    deleteButton.setTitle("Del", for: UIControlState())
    deleteButton.addTarget(self, action: #selector(DynamStackVC.deleteStackView(_:)), for: .touchUpInside)

    stack.addArrangedSubview(dateLabel)
    stack.addArrangedSubview(numberLabel)
    stack.addArrangedSubview(deleteButton)

    return stack
  }
}

Код на основе раскадровки (кнопка «Добавить элемент» работает всегда)

import UIKit

class DynamStackVC: UIViewController {

    // MARK: Properties

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var stackView: UIStackView!

    // MARK: UIViewController

    override func viewDidLoad() {
      super.viewDidLoad()

      // Set up the scrollview.
      let insets = UIEdgeInsets(top: 20, left: 0.0, bottom: 0.0, right: 0.0)
      scrollView.contentInset = insets
      scrollView.scrollIndicatorInsets = insets
    }

    // MARK: Interface Builder actions

    @IBAction func addEntry(_: AnyObject) {
      guard let addButtonContainerView = stackView.arrangedSubviews.last else { fatalError("Expected at least one arranged view in the stack view.") }
      let nextEntryIndex = stackView.arrangedSubviews.count - 1

      let offset = CGPoint(x: scrollView.contentOffset.x, y: scrollView.contentOffset.y + addButtonContainerView.bounds.size.height)

      let newEntryView = createEntryView()
      newEntryView.isHidden = true

      stackView.insertArrangedSubview(newEntryView, at: nextEntryIndex)

      UIView.animate(withDuration: 0.25, animations: {
        newEntryView.isHidden = false
        self.scrollView.contentOffset = offset
      })
    }

    func deleteStackView(_ sender: UIButton) {
      guard let entryView = sender.superview else { return }

      UIView.animate(withDuration: 0.25, animations: {
        entryView.isHidden = true
      }, completion: { _ in
        entryView.removeFromSuperview()
      })
    }

    // MARK: Convenience

    /// Creates a horizontal stack view entry to place within the parent `stackView`.
    fileprivate func createEntryView() -> UIView {
      let date = DateFormatter.localizedString(from: Date(), dateStyle: .short, timeStyle: .none)
      let number = UUID().uuidString

      let stack = UIStackView()
      stack.axis = .horizontal
      stack.alignment = .center
      stack.distribution = .fillProportionally
      stack.spacing = 8

      let dateLabel = UILabel()
      dateLabel.text = date
      dateLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)

      let numberLabel = UILabel()
      numberLabel.text = number
      numberLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.caption2)
      numberLabel.setContentHuggingPriority(UILayoutPriorityDefaultLow - 1.0, for: .horizontal)
      numberLabel.setContentCompressionResistancePriority(UILayoutPriorityDefaultHigh - 1.0, for: .horizontal)

      let deleteButton = UIButton(type: .roundedRect)
      deleteButton.setTitle("Del", for: UIControlState())
      deleteButton.addTarget(self, action: #selector(DynamStackVC.deleteStackView(_:)), for: .touchUpInside)

      stack.addArrangedSubview(dateLabel)
      stack.addArrangedSubview(numberLabel)
      stack.addArrangedSubview(deleteButton)

      return stack
    }
}

enter image description here

0
Tony M 14 Июл 2017 в 03:07

1 ответ

Лучший ответ

Я понял это, поместив цвета фона на все кнопки и метки в динамическом stackView. Как вы можете видеть на новом анимированном GIF-изображении, голубой цвет кнопки «Добавить элемент» исчезает после первого нажатия кнопки. Чтобы подтвердить, я удвоил исходную высоту кнопки (то есть с (25) до (50) слева на гифке), что затем позволило нажать две кнопки, прежде чем она перестанет работать, и голубой фон исчезнет. Это научило меня многому о том, как работает динамический stackView, и я надеюсь, что это поможет кому-то другому. введите описание изображения здесь

0
Tony M 14 Июл 2017 в 06:30
Если вы решили свою проблему, отметьте ответ как принятый, чтобы сообщить другим, что проблема решена.
 – 
Anh Pham
14 Июл 2017 в 10:07
1
Я попробовал это сразу после публикации своего ответа, но система пока не позволяет мне это сделать, показывая красную ошибку: «Вы можете принять свой ответ через 2 дня».
 – 
Tony M
14 Июл 2017 в 18:30
Ой, простите. Это мой недостаток. Я не обратил внимания на время публикации вопроса. Пожалуйста, отметьте ответ как принятый, когда сможете. Спасибо!
 – 
Anh Pham
14 Июл 2017 в 22:40