web-dev-qa-db-ja.com

Swift:stackViewからのビューの消失

私のメインのストーリーボードにはかなりシンプルな設定があります:

  • 3つのビューを含むスタックビュー
  • 最初のビューは高さが固定されており、セグメントコントローラが含まれています
  • 他の2つのビューには制限がありません。一度に1つだけがアクティブになり、使用可能なスペースを埋めるという考え方です。

次のようにビューのアクティブビューの変更を処理するコードがあります。

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")")
        }
    }
}

ご覧のように、「First」というタブを選択すると、インデックス1のサブビューが表示され、2は非表示になります。それ以外を選択すると、インデックス2のサブビューが表示され、1は非表示になります。

これは、ビューをゆっくりと変更すると最初は機能するように見えますが、少し速くすると、数回クリックした後、インデックス1のビューが永続的に非表示のままになり、インデックス0のビューが画面全体に表示されます。問題を示すアニメーションとストーリーボードのスクリーンショットを下に配置しました。出力は、問題が発生した場合、最初のセグメントをクリックすると両方のビューが非表示のままであることを示しています。

なぜこれが起こっているのか誰かに教えてもらえますか?これはバグですか、それとも私はあるべきことをしていませんか?

よろしくお願いします!

Animation showing issue

Image of stack view in storyboard

更新:最初のセグメント> 2番目のセグメント> 3番目のセグメント> 2番目のセグメント>最初のセグメントの順に移動すると、問題を確実に再現できるようです。

14
Ben

結局、ここですべての提案を試しても、なぜこのように動作していたのかわからなかったため、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
Ben

バグは、スタックビューでのビューの非表示と表示が累積されることです。奇妙なAppleバグ。スタックビューでビューを2回非表示にした場合、元に戻すには2回表示する必要があります。3回表示した場合は、3回非表示にする必要があります。実際にはそれを隠します(開始時に隠されていたと仮定します)。

これはアニメーションの使用とは無関係です。

したがって、コードで次のようなことを行い、表示されている場合にのみビューを非表示にすると、この問題を回避できます。

if myView.isHidden == false {
    myView.isHidden = true
}
41
Dave Batton

Dave Battonによるいい答えに基づいて、UIView拡張機能を追加して、呼び出しサイトを少しすっきりとしたIMOにすることもできます。

extension UIView {

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

次に、stackView.subviews[someIndex].isHiddenInStackView = falseを呼び出すことができます。これは、ifステートメントの束ではなく、スタックビュー内で管理するビューが複数ある場合に役立ちます。

8
Sandy Chapman

私が見ることができることに基づいて、この奇妙な動作はアニメーションの継続時間によって引き起こされます。ご覧のように、アニメーションが完了するまで1秒かかりますが、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
    }
}

これは高速な切り替え(欠点として見えるかもしれません)を防ぎますが、これを解決するために私が知っている唯一の他の方法は、アニメーションを完全に削除することです。

1
Benjamin Lowry

スタックビューとサブビュー、特にセグメント化されたコントロールの構成と自動レイアウト制約を確認します。

セグメント化されたコントロールはスタックビューの設定を複雑にするので、セグメント化されたコントロールをスタックビューから取り出し、メインビューに対して制約を設定します。

セグメント化されたコントロールがスタックビューの外にある場合、コードが正しく機能するようにスタックビューを設定するのは比較的簡単です。

スタックビューの制約をリセットして、セグメント化されたコントロールの下に配置し、スーパービューの残りの部分をカバーします。属性インスペクタで、配置を塗りつぶしに、分布を均等に塗りつぶし、コンテンツモードを塗りつぶしにスケールに設定します。

サブビューの制約を削除し、コンテンツモードを[塗りつぶしにスケール]に設定します。

コード内のArrangedSubviewのインデックスを調整すると、自動的に機能するはずです。

0
H. M. Madrone