2017-09-15 50 views
1

儘可能簡單,我仍然發現自己正努力尋找正確的解決方案。強制自動佈局以正確更新UIView框架viewDidLoad

我想了解什麼是使用自動佈局時,發現裏面viewDidLoadREALUIView(或任何其他子視圖)框架正確方式。

主要問題是,在viewDidLoad中,視圖不會應用它們的約束。我知道,「已知」答案這種情況

override func viewDidLoad() { 
     super.viewDidLoad() 
    view.layoutIfNeeded() 
    stepContainer.layoutIfNeeded() // We need this Subview real frame! 
    let realFrame = stepContainer.frame 
} 

但我發現它並不總是工作,並且不時給它的錯誤幀(即不顯示的最後一幀)。

經過一番研究,我發現在DispatchQueue.main.async { }下翹曲這段代碼給出了準確的結果。但我不確定這是否是正確的方式來處理這個問題,或者我是否正在使用這種方式引發某些內部問題。最後的「工作」代碼:

override func viewDidLoad() { 
     super.viewDidLoad() 

     DispatchQueue.main.async { 
      self. stepContainer.layoutIfNeeded() 
      print(self. stepContainer.frame) // Real frame.. 
     } 

} 

注:我需要找到的是隻能從viewDidLoad中真正的框架,請不建議使用viewDidAppear/layoutSubviews等

+3

viewDidLoad不是設置需要框架知識的地方,這就是爲什麼你有這個問題 – trapper

+0

@trapper我意識到這一點。由於一個特定的場景,我需要解決這個問題(它是可以解決的)..我只是確保我已經以正確的方式解決了這個問題 –

+0

你在這個視圖的生命週期早期嘗試使用框架來做什麼? –

回答

2

正如@DavidRönnqvist指出

原因調度異步這裏給你「正確」的價值在於,它 計劃獲得在未來的運行循環運行;之後viewWillAppearviewDidLayoutSubviews已運行。

override func viewDidLoad() { 
    super.viewDidLoad() 
    DispatchQueue.main.async { 
    print("DispatchQueue.main.async viewDidLoad") 
    } 
} 

override func viewWillAppear(_ animated: Bool) { 
    super.viewWillAppear(animated) 

    print("viewWillAppear") 
} 

override func viewDidAppear(_ animated: Bool) { 
    super.viewDidAppear(animated) 

    print("viewDidAppear") 
} 

override func viewDidLayoutSubviews() { 
    super.viewDidLayoutSubviews() 

    print("viewDidLayoutSubviews") 
} 

viewWillAppear中

viewDidLayoutSubviews

viewDidAppear

DispatchQueue.main.async viewDidLoad中

DispatchQueue.main.async內部的代碼從viewDidLoad甚至在viewDidAppear之後調用。因此,在viewDidLoad中使用DispatchQueue.main.async可以爲您提供正確的框架,但不盡可能早。

回答

  • 如果你想盡早得到正確的框架,viewDidLayoutSubviews是做了正確的地方。

  • 如果你必須把一些代碼放在viewDidLoad裏面,你就是正確的做法。看起來像DispatchQueue.main.async是最好的辦法。

1

看完後評論,也許你可以試試您的視圖控制器嵌入到另外一個與容器和做這樣的事情:

  • 在父視圖控制器中加一個變量:var viewSize : CGSize?
  • 在子視圖控制器中加一個變量:var parentViewSize : CGSize?
  • 在父視圖控制器,獲得大小在viewDidLayoutSubViews並儲存起來:viewSize = view.size
  • 在父視圖控制器,在prepareForSegue送你存儲的大小: (destinationViewController as? MyViewController)?.parentViewSize = viewSize
  • 在子視圖控制器,在viewDidLoad你會在它

難道做了良好的價值訪問parentViewSize變量?

1

你的問題類似於this problem,我的答案也是一樣的。

frame不能保證viewDidLoad與view最終顯示時相同。 UIKit根據將出現的上下文來調整視圖控制器視圖的框架,然後再呈現它。爲了更好地理解視圖生命週期,您可以參考此圖像。 enter image description here

該圖像將幫助您理解代碼的工作原理。如圖所示,viewWillAppear在加載視圖後被調用,並且當您設置異步調度時,會在執行viewWillAppear後異步添加到線程中。所以,一旦viewWillAppear被調用,你的框架將根據你的視圖進行更新,並且你在調度異步時獲得正確的框架。

參考文獻: Image source

For more information about view life cycle you can visit this answer

所以,在最後,你可以去以下兩種選擇:

  1. 如果你想使用自動佈局則無論是在viewDidLayoutSubviews管理框架或viewWillAppear。 (在任何一個viewDidLayoutSubview中,viewWillAppear都會被調用,每當你的視圖位於視圖頂層時,它就不應該被優先選擇)。
  2. 如果您要跳過自動佈局,那麼您可以通過編程方式創建和維護視圖。

希望這會有所幫助!

+0

你應該看看這個問題https://stackoverflow.com/questions/14060002/wrong-frame-size-in-viewdidload。 (1)我認爲'viewWillAppear'不是正確的選擇。 – trungduc

+0

同意,這就是爲什麼我提到。我會編輯它以更精確。感謝您指出。 –

+0

這裏似乎有誤解;)。我的意思是*在ios6中使用自動佈局時,在子視圖佈置完成之前,幀不會被設置。在這些條件下獲取幀數據的正確位置是viewController方法'viewDidLayoutSubviews' *。它的答案是**更新(ios6)**。 – trungduc