簡體   English   中英

UILayoutViewController中的滾動時自動布局約束“消失”

[英]Autolayout Constraints “disappear” on scroll in UIPageViewController

我正在嘗試使用自動布局使用“浮動”標題構建滾動視圖。 更確切地說,我正在嘗試建立一個包含幾列的日歷視圖。 這些列中的每一列都應具有自己的標題,該標題應浮動在頂部,而該列可以垂直滾動到其下方。

如果一切正常,則如下所示: 日歷顯示正常工作

如您所見,有幾列和一個頁面控件,用於指示可用的頁面數。

但是,在滑動/平移(甚至只是嘗試滑動)以切換到下一頁時,將標題標簽保持在頂部的約束將被刪除,並且它們會消失到滾動視圖的頂部(在情節提要中)。

日歷視圖第1部分損壞 列標題不可見,因為在屏幕外滾動

日歷視圖第2部分損壞 列標題位於滾動視圖的頂部(高度錯誤)。

設定

用戶可以在日期之間進行切換(頂部帶有左右箭頭的“今天”按鈕),並可以在顯示的人員之間進行切換。 (在視圖中滑動/平移)Boh交互是通過UIPageViewController內部實現的。 外頁視圖控制器在日期之間切換,內頁控制器在列的頁面之間(帶有人)切換。 時間視圖包含在外部頁面視圖控制器中,而不包含在內部頁面視圖控制器中。

因此,視圖和控制器的層次結構如下所示:

Calendar PageViewController (the one controlled via buttons in the navigation bar)
-- Scroll View of Page View Controller
---- Team View Controller (with view)
------ Header View reference (see screenshot 2)
------ Scroll View for vertical scrolling
-------- Time View (the one on the left)
-------- People PageViewController (the one controlled by swiping left/right)
---------- ScrollView of Page View Controller
------------ ViewController for a Single Page (with view)
--------------(1-n) Container View Controller (several of those, in the example 4 per page)
---------------- Column View Controller
------------------ Header View (A UIVisualEffectsView with a label)
------------------ calendar column view (the one doing the horizontal stripes)

將各個列視圖控制器的標題視圖兩個固定在頂部,我在內部頁面視圖控制器外部和垂直滾動視圖外部使用了一個參考視圖。 我在上面的概述中將其稱為Header View reference ,您可以在殘破的示例中很好地看到它:

標題視圖參考

這是一個簡單的UIVisualEffectsView,我將其限制在左上方,並且高度和寬度與時間視圖相同。 因此,此視圖具有正確的位置和高度,我希望所有標題視圖都具有正確的位置和高度,並使用它在每個ColumViewControllerupdateViewConstraints方法中的代碼中將各個列標題視圖約束到它(所有其他約束都在情節updateViewConstraints中設置)。所以:

- (void)updateViewConstraints
{
    DDLogDebug(@"updating view constraints in colum view controller for %@", self.employee.displayName);
    UIView *baseView = self.headerViewReferenceContainer.viewForHeaderContainerViewConstraints;
    UIView *containerReference = self.headerViewReferenceContainer.headerContainerViewReference;
    NSParameterAssert([containerReference isDescendantOfView:baseView] && [self.headerContainerView isDescendantOfView:baseView]);
    [baseView addConstraint:[NSLayoutConstraint constraintWithItem:self.headerContainerView
                                                     attribute:NSLayoutAttributeTop
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:containerReference
                                                     attribute:NSLayoutAttributeTop
                                                        multiplier:1.0
                                                          constant:0]];

    [baseView addConstraint:[NSLayoutConstraint constraintWithItem:self.headerContainerView
                                                         attribute:NSLayoutAttributeHeight
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:containerReference
                                                     attribute:NSLayoutAttributeHeight
                                                        multiplier:1.0
                                                          constant:0]];

    [super updateViewConstraints];
}

baseView是應將約束添加到的視圖。 它必須是標題引用視圖和列視圖控制器的標題視圖的超級視圖。 目前,這將是上方Team View Controller的視圖(一個包含垂直滾動視圖的視圖)。

containerReference是“標題視圖參考”視圖,如上面的屏幕快照所示。

因此,我將列標題視圖限制為與參考視圖具有相同的頂部位置和高度。 (寬度和x位置取決於列)

正如您在破碎的屏幕截圖中所看到的那樣,Header View參考始終處於正確位置。 另外,在加載視圖控制器和那里的視圖時,約束已正確設置。

然后,當開始平移手勢以切換PeoplePageView Controller的頁面並ViewController for a Single Page加載下一個ViewController for a Single Page約束將以某種方式消失,並且標題視圖由於不再固定在頂部而移至滾動視圖的頂部。

下一個View Controller for a Single Page的約束已正確設置,但直到其卡入到位。

平移時

因此,在平移時,正確設置了視圖控制器要顯示的約束。 但是,只要它卡入到位(就像在-viewDidAppear發生的-viewDidAppear ),約束也會被刪除。

我不明白為什么以及如何刪除約束。

什么沒錯/我嘗試了什么

這可能是由於其中一種相關的觀點消失了,但是

  • baseView根本不改變,因為它是分頁滾動視圖的baseView視圖
  • 標頭參考視圖未更改,因為它不包含在頁面頁面視圖控制器中。
  • 在視圖控制器完全脫離屏幕之前,標題列視圖不會消失,因此在分頁期間布局不應中斷

我曾經遇到過這樣的問題,即布局僅在分頁完成之后才落入適當位置。 這是由於我的約束與頂部布局約束有關,如iOS 8 UIPageViewController過渡后應用約束中所述 結果,我使用了到超級視圖頂部的固定距離來固定Header View Reference而不是將其固定到頂部布局約束。

因此,UIPageViewController的錯誤在頁面調度上沒有正確的頂部布局約束也不應該成為問題。

我沒有以編程方式添加任何視圖。 我所有使用自動布局的情節提要板和視圖控制器都是通過情節提要中的Container View Controllers相互添加的,或者實現UIPageViewControllerDatasource並從xib實例化視圖控制器。 因此,不應有任何與translatesAutoResizingMasks相關的問題。 另外,它在加載時有效,僅在分頁時中斷,這不適合通常的translatesAutoResizingMasks = YES問題。

我也沒有收到關於沖突約束的任何錯誤,或者沒有為約束准備視圖層次,只是刪除了工作約束。

由於我有一個問題,我認為這是不相關的,但我會在這里列出來為完整起見:我得到一個錯誤,由於列標題的看法里面的標簽被添加到早期的布局限制:

The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7af4df20 UILabel:0x7af4e0d0'Susanne'.width == UIVisualEffectView:0x7af4de70.width>
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.

但是,該消息抱怨標簽及其包含的視圖(列的標題視圖)之間的關系,而不是標題視圖的“外部”約束。 此外,它警告說,如果過早解決約束,應用程序將崩潰。 該應用程序不會崩潰,因此時機似乎正確。 另外,應用程序抱怨的約束條件是在情節提要中設置的,因此我看不到如何弄錯時間。

tl; dr:從UIPageViewController內部到視圖外部的視圖層次結構的多個層次的約束都將被刪除(沒有警告,注釋,原因或其他任何東西)。 我不明白為什么以及如何防止這種情況發生,或者在什么時候再次添加它們。

更新:

我向viewWillAppear:添加了以下調用:

[self.view setNeedsUpdateConstraints]

這具有以下效果:

  • 當將頁面稍微拖動到一側(不切換頁面)並再次釋放它(以便它回彈)時,損壞的約束將再次得到修復(並且布局是固定的)。 這是由於頁面視圖控制器在原始視圖控制器上“后退”(以抵消先前發出的viewWillDisappear調用)上調用viewWillAppear:所致。
  • 但是在滾動過程中,布局仍然損壞。
  • 同樣,當滑動到新頁面時,新頁面的約束也會被破壞。
  • 但是每當調用viewWillAppear ,約束都會在短時間內固定,直到再次將其刪除(通常在滾動結束時)。

如果我將相同的調用添加到viewDidAppear 而不是 viewWillAppear viewWillAppear發生以下情況:

  • 這導致布局在大多數時間都是正確的(滾動結束后它們是固定的)。
  • 但是在滾動過程中它仍然被破壞。

現在,如果我將[self.view setNeedsUpdateConstraints]添加到這兩種方法中,則會發生以下情況:

  • 布局幾乎始終都能正確運行,不會出現奇怪的視圖跳動
  • 除了第一頁更改。 在此期間,原始頁面的約束似乎已被刪除,並且布局以上面顯示的方式破壞了。 在那之后,約束似乎被永久性地固定了,來回滑動作品沒有問題。
  • 即使我很確定頁面視圖控制器不會將所有三個頁面都保留在內存中,當分頁到最后一頁(並嘗試超越它)並返回時,對於三個頁面(我也嘗試過四個)都是如此。

是否有可能updateViewConstraints調用updateViewConstraints導致多次添加相同的約束,從而導致問題。 我相信這很容易發生。

我在許多討論中注意到,許多人似乎建議不要在此函數中進行創建和添加(viewDidLoad似乎是首選),並且僅在此函數中進行約束值設置,以最大程度地減少多次調用的問題,但要確保大小正確。 另一個選擇似乎是為控制器添加約束的布爾值保護,以確保它在控制器的生命周期中僅發生一次。

可能不是您的問題,但值得一提。

嘗試在viewDidLayoutSubviews設置約束。 我不太確定,但我記得以這種方式解決了與約束有關的問題,而且似乎可行。

為什么不將headerView的頂部設置為baseview的頂部。 即包含滾動視圖的視圖。 這肯定會解決您的問題。

好的,我仍然認為這是一個錯誤,但是我找到了一種解決方法:

我基本上有兩個要保留的約束,有時會消失。 我為這些創建屬性,但使它們變

@property (weak) NSLayoutConstraint *headerViewHeightConstraint;
@property (weak) NSLayoutConstraint *headerViewTopConstraint;

然后,我將創建的約束分配給updateViewConstraints這些屬性,如下所示:

NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.headerContainerView
                                                                 attribute:NSLayoutAttributeTop
                                                                 relatedBy:NSLayoutRelationEqual
                                                                    toItem:containerReference
                                                                 attribute:NSLayoutAttributeTop
                                                                multiplier:1.0
                                                                  constant:0];
[baseView addConstraint:topConstraint];
self.headerViewTopConstraint = topConstraint;

通過使用weak引用,我可以檢查約束是否存在,如果沒有其他約束可以引用,則不保留約束。 這樣,當從視圖中刪除約束時(無論出於何種原因),它將被解除分配,而我的屬性將為nil。 我可以使用它來檢查是否需要像這樣重新建立約束:

- (void)reestablishHeaderViewConstraintsIfNecessary
{
    if (!self.headerViewTopConstraint || !self.headerViewHeightConstraint) // if any of the constraints is missing
    {
        [self.view setNeedsUpdateConstraints];
    }
}

現在,我只需要一個地方可以從中調用該方法,以確保在可能已刪除約束的任何時候都可以調用該方法。 如問題中所述, viewWillAppearviewDidAppear不夠好。 相反,我使用viewWillLayoutSubviews 每當邊界發生變化時(例如,滾動期間多次),就會調用此方法,這是很常見的,但是由於我包裝了實際的約束失效,因此如果不需要執行任何操作,這應該足夠便宜:

- (void)viewWillLayoutSubviews
{
    [self reestablishHeaderViewConstraintsIfNecessary];
    [super viewWillLayoutSubviews];
}

我想用它是非常重要的viewWillLayoutSubviews ,不viewDidLayoutSubviews廣告無效布局viewDidLayoutSubviews導致了異常,但我不知道。 這也是為什么我將調用放在對[super viewWillLayoutSubviews]的調用之前的原因,但是我沒有嘗試過將其與隨后出現的調用一起使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM