Swift: Исчезающие виды из stackView

У меня довольно простая настройка в моей основной раскадровке:

  • Представление стека, которое включает три представления
  • Первый вид имеет фиксированную высоту и содержит контроллер сегмента
  • Два других представления не имеют ограничений, идея состоит в том, что одновременно будет активен только один, и, таким образом, он заполнит доступное пространство

У меня есть код, который будет иметь дело с изменением представления активных представлений следующим образом:

import Foundation
import UIKit
class ViewController : UIViewController {
    @IBOutlet weak var stackView: UIStackView!
    @IBOutlet weak var segmentController: UISegmentedControl!
    @IBAction func SegmentClicked(_ sender: AnyObject) {
        updateView(segment: sender.titleForSegment(at: sender.selectedSegmentIndex)!)
    }
    override func viewDidLoad() {
        updateView(segment: "First")
    }
    func updateView(segment: String) {
        UIView.animate(withDuration: 1) {
            if(segment == "First") {
                self.stackView.arrangedSubviews[1].isHidden = false
                self.stackView.arrangedSubviews[2].isHidden = true
            } else {
                self.stackView.arrangedSubviews[1].isHidden = true
                self.stackView.arrangedSubviews[2].isHidden = false

            }
            print("Updating views")
            print("View 1 is \(self.stackView.arrangedSubviews[1].isHidden ? "hidden" : "visible")")
            print("View 2 is \(self.stackView.arrangedSubviews[2].isHidden ? "hidden" : "visible")")
        }
    }
}

Как вы можете видеть, когда выбрана вкладка "Первая", должно отображаться подпредставление с индексом 1, тогда как 2 скрыто, а когда выбрано все остальное, должно отображаться подвид с индексом 2, а 1 скрыто..

Поначалу кажется, что это работает, если я медленно изменяю представления, но если я иду немного быстрее, представление с индексом 1, кажется, остается постоянно скрытым после нескольких щелчков, в результате чего представление с индексом 0 покрывает весь экран. Я разместил анимацию, показывающую проблему, и скриншот раскадровки ниже. Вывод показывает, что при возникновении проблемы оба вида остаются скрытыми при нажатии на первый сегмент.

Кто-нибудь может сказать мне, почему это происходит? Это ошибка, или я не делаю то, что должен быть?

Большое спасибо заранее!

Animation showing issue

Image of stack view in storyboard

Обновление. Кажется, я могу надежно воспроизвести проблему, перейдя к первым> вторым> третьим> вторым> первым сегментам в указанном порядке.

Ответ 1

В конце концов, попробовав все предложения здесь, я все еще не мог понять, почему так поступает, поэтому я связался с Apple, который попросил меня подать отчет об ошибке. Тем не менее, я нашел работу, сначала открыв оба взгляда, и решил мою проблему:

func updateView(segment: String) {
    UIView.animate(withDuration: 1) {
        self.stackView.arrangedSubviews[1].isHidden = false
        self.stackView.arrangedSubviews[2].isHidden = false
        if(segment == "First") {
            self.stackView.arrangedSubviews[2].isHidden = true
        } else {
            self.stackView.arrangedSubviews[1].isHidden = true
        }
    }
}

Ответ 2

Ошибка заключается в том, что скрытие и отображение представлений в представлении стека является кумулятивным. Странная ошибка Apple. Если вы дважды скроете представление в стеке, вам нужно дважды отобразить его, чтобы вернуть его. Если вы показываете его три раза, вам нужно скрыть его три раза, чтобы скрыть его (при условии, что он был скрыт для запуска).

Это не зависит от использования анимации.

Итак, если вы делаете что-то подобное в своем коде, только скрывая представление, если оно видно, вы избежите этой проблемы:

if myView.isHidden == false {
    myView.isHidden = true
}

Ответ 3

Основываясь на приятном ответе Дейва Баттона, вы также можете добавить расширение UIView, чтобы сделать сайт вызова немного более чистым, IMO.

extension UIView {

    var isHiddenInStackView: Bool {
        get {
            return isHidden
        }
        set {
            if isHidden != newValue {
                isHidden = newValue
            }
        }
    }
}

Затем вы можете вызвать stackView.subviews[someIndex].isHiddenInStackView = false, что полезно, если у вас есть несколько представлений для управления в представлении стека по сравнению с кучей операторов if.

Ответ 4

Основываясь на том, что я вижу, это странное поведение вызвано продолжительностью анимации. Как вы можете видеть, для завершения анимации требуется одна секунда, но если вы начнете переключать segmentControl быстрее, чем это, я бы сказал, что это вызывает такое поведение.

Что вам нужно сделать, так это отключить интерактивность пользователя при вызове метода, а затем снова включить его после завершения анимации.

Он должен выглядеть примерно так:

func updateView(segment: String) {

    segmentControl.userInteractionEnabled = false
    UIView.animateWithDuration(1.0, animations: {
        if(segment == "First") {
            self.stackView.arrangedSubviews[1].isHidden = false
            self.stackView.arrangedSubviews[2].isHidden = true
        } else {
            self.stackView.arrangedSubviews[1].isHidden = true
            self.stackView.arrangedSubviews[2].isHidden = false

        }
        print("Updating views")
        print("View 1 is \(self.stackView.arrangedSubviews[1].isHidden ? "hidden" : "visible")")
        print("View 2 is \(self.stackView.arrangedSubviews[2].isHidden ? "hidden" : "visible")")
    }, completion: {(finished: Bool) in
        segmentControl.userInteractionEnabled = true
    }
}

В то время как это предотвратит быстрое переключение (которое вы можете увидеть как недостаток), единственный другой способ, который я знаю об этом, заключается в полном удалении анимаций.

Ответ 5

Проверьте настройки и ограничения автоопределения в представлении стека и в подзонах, особенно в сегментированном элементе управления.

Сегментированный элемент управления усложняет настройку представления стека, поэтому я бы взял сегментированный элемент управления из представления стека и установил его ограничения относительно основного вида.

С сегментированным контролем из представления стека, довольно просто настроить представление стека, чтобы ваш код работал правильно.

Reset ограничения на представление стека, чтобы он располагался ниже сегментированного элемента управления и покрывал остальную часть супервизора. В Инспекторе атрибутов установите для параметра "Выравнивание для заполнения", "Распределение для заполнения равно" и "Режим содержимого" для масштабирования для заполнения.

Удалите ограничения в подзонах и установите их режим содержимого на шкалу для заполнения.

Откорректируйте индексирование на организованных представлениях в вашем коде, и оно должно работать автоматически.