Swift: UICollection을 새로 고치는 방법장치 회전 후 레이아웃 보기
UICollectionView(플로 레이아웃)를 사용하여 간단한 레이아웃을 작성했습니다.각 셀의 너비는 다음을 사용하여 화면의 너비로 설정됩니다.self.view.frame.width
하지만 장치를 돌리면 셀이 업데이트되지 않습니다.
방향 변경 시 호출되는 함수를 찾았습니다.
override func willRotateToInterfaceOrientation(toInterfaceOrientation:
UIInterfaceOrientation, duration: NSTimeInterval) {
//code
}
그러나 UICollectView 레이아웃을 업데이트할 수 없습니다.
주요 코드는 다음과 같습니다.
class ViewController: UIViewController , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{
@IBOutlet weak var myCollection: UICollectionView!
var numOfItemsInSecOne: Int!
override func viewDidLoad() {
super.viewDidLoad()
numOfItemsInSecOne = 8
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
//print("orientation Changed")
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numOfItemsInSecOne
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellO", forIndexPath: indexPath)
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{
let itemSize = CGSize(width: self.view.frame.width, height: 100)
return itemSize
}}
다음 기능을 추가합니다.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
방향을 변경하면 이 기능이 호출됩니다.
더 나은 선택은 전화하는 것입니다.invalidateLayout()
대신에reloadData()
셀 재생을 강제하지 않기 때문에 성능이 약간 향상됩니다.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
또한 이러한 방법으로 무효화할 수 있습니다.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self.collectionView.collectionViewLayout invalidateLayout];
}
WillLayoutSubviews() 보기가 작동하지 않았습니다.DidLayoutSubviews()도 볼 수 없습니다.둘 다 앱을 무한 루프 상태로 만들어 인쇄 명령을 사용하여 확인했습니다.
작동하는 방법 중 하나는
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Reload here
}
언제UICollectionLayout
경계 변경을 감지하면 유효하지 않은 레이아웃의 경로를 다시 지정해야 하는지 여부를 묻습니다.메소드를 직접 다시 작성할 수 있습니다.UICollectionLayout
부를 수 있습니다invalidateLayout
적절한 시기의 방법
class CollectionViewFlowLayout: UICollectionViewFlowLayout{
/// The default implementation of this method returns false.
/// Subclasses can override it and return an appropriate value
/// based on whether changes in the bounds of the collection
/// view require changes to the layout of cells and supplementary views.
/// If the bounds of the collection view change and this method returns true,
/// the collection view invalidates the layout by calling the invalidateLayout(with:) method.
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return (self.collectionView?.bounds ?? newBounds) != newBounds
}
}
업데이트하기UICollectionViewLayout
,traitCollectionDidChange
방법을 사용할 수도 있습니다.
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
guard previousTraitCollection != nil else { return }
collectionView?.collectionViewLayout.invalidateLayout()
}
이 점 양해 부탁드립니다.
traitCollectionDidChange(이전의 traitCollection :)는 크기 클래스가 세로 방향과 가로 방향 모두에서 .정규이기 때문에 회전할 때 iPad에서 호출되지 않습니다.컬렉션 뷰 크기가 변경될 때마다 호출되는 WillTransition(to:with:) 뷰가 있습니다.
또한 앱이 전체 화면을 차지하지 않을 수 있으므로 멀티태스킹을 지원하는 경우에는 UIScreen.mainScreen().bounds를 사용하지 마십시오. collectionView.frame을 사용하는 것이 좋습니다.그것을 위한 폭.
내 코드:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.collectionView.collectionViewLayout.invalidateLayout()
}
올바르게 작동합니다!!!
부르기viewWillLayoutSubviews
최적이 아닙니다.전화해 보십시오.invalidateLayout()
방법 우선.
만약 당신이 그것을 경험한다면.The behaviour of the UICollectionViewFlowLayout is not defined
오류, 새 레이아웃에 따라 뷰 내의 모든 요소가 크기를 변경했는지 확인해야 합니다.(예시 코드 내의 선택적 단계 참조)
시작하기 위한 코드입니다.UI가 생성되는 방법에 따라 다음을 호출하는 데 적합한 보기를 찾기 위해 실험해야 할 수도 있습니다.recalculate
방법, 하지만 그것은 당신의 첫 단계로 당신을 안내할 것입니다.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
/// (Optional) Additional step 1. Depending on your layout, you may have to manually indicate that the content size of a visible cells has changed
/// Use that step if you experience the `the behavior of the UICollectionViewFlowLayout is not defined` errors.
collectionView.visibleCells.forEach { cell in
guard let cell = cell as? CustomCell else {
print("`viewWillTransition` failed. Wrong cell type")
return
}
cell.recalculateFrame(newSize: size)
}
/// (Optional) Additional step 2. Recalculate layout if you've explicitly set the estimatedCellSize and you'll notice that layout changes aren't automatically visible after the #3
(collectionView.collectionViewLayout as? CustomLayout)?.recalculateLayout(size: size)
/// Step 3 (or 1 if none of the above is applicable)
coordinator.animate(alongsideTransition: { context in
self.collectionView.collectionViewLayout.invalidateLayout()
}) { _ in
// code to execute when the transition's finished.
}
}
/// Example implementations of the `recalculateFrame` and `recalculateLayout` methods:
/// Within the `CustomCell` class:
func recalculateFrame(newSize: CGSize) {
self.frame = CGRect(x: self.bounds.origin.x,
y: self.bounds.origin.y,
width: newSize.width - 14.0,
height: self.frame.size.height)
}
/// Within the `CustomLayout` class:
func recalculateLayout(size: CGSize? = nil) {
estimatedItemSize = CGSize(width: size.width - 14.0, height: 100)
}
/// IMPORTANT: Within the `CustomLayout` class.
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
guard let collectionView = collectionView else {
return super.shouldInvalidateLayout(forBoundsChange: newBounds)
}
if collectionView.bounds.width != newBounds.width || collectionView.bounds.height != newBounds.height {
return true
} else {
return false
}
}
다음을 사용하여 UICollectionView 레이아웃을 업데이트할 수 있습니다.
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if isLandscape {
return CGSizeMake(yourLandscapeWidth, yourLandscapeHeight)
}
else {
return CGSizeMake(yourNonLandscapeWidth, yourNonLandscapeHeight)
}
}
저한테는 효과가 있어요.그리고 이것은 목표-C의 코드는 다음과 같습니다.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[collectionView.collectionViewLayout invalidateLayout];
}
저도 약간의 문제가 있었지만 다음을 사용하여 해결되었습니다.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionViewFlowLayoutSetup(with: view.bounds.size.width)
collectionView?.collectionViewLayout.invalidateLayout()
collectionViewFlowLayoutSetup(with: size.width)
}
fileprivate func collectionViewFlowLayoutSetup(with Width: CGFloat){
if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: Width, height: 300)
}
}
화면 방향이 바뀌면 알림을 설정하고 화면 방향에 따라 항목 크기를 설정하는 셀을 다시 로드하고 이전 셀에 인덱스 경로를 설정하여 해결합니다.이것은 흐름 레이아웃에서도 작동합니다.제가 작성한 코드는 다음과 같습니다.
var cellWidthInLandscape: CGFloat = 0 {
didSet {
self.collectionView.reloadData()
}
}
var lastIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
cellWidthInLandscape = UIScreen.main.bounds.size.width
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func rotated() {
// Setting new width on screen orientation change
cellWidthInLandscape = UIScreen.main.bounds.size.width
// Setting collectionView to previous indexpath
collectionView.scrollToItem(at: IndexPath(item: lastIndex, section: 0), at: .right, animated: false)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// Getting last contentOffset to calculate last index of collectionViewCell
lastIndex = Int(scrollView.contentOffset.x / collectionView.bounds.width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Setting new width of collectionView Cell
return CGSize(width: cellWidthInLandscape, height: collectionView.bounds.size.height)
}
동시에 선택한 셀은 가로 방향에서 세로 방향으로 변경된 후 중앙에 배치되지 않습니다.저는 이 문제를 다음과 같이 해결합니다.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
//Scroll to current Item after the orientation change. Sometimes the current item is not centered.
coordinator.animate(alongsideTransition: { context in
self.housesCollectionView.collectionViewLayout.invalidateLayout()
self.housesCollectionView.scrollToItem(at: self.selectedHouseIndexPath, at: .left, animated: true)
})
}
아래 방법으로 문제를 해결했습니다.
override func viewDidLayoutSubviews() {
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
collectionView.collectionViewLayout.invalidateLayout()
collectionView.collectionViewLayout = flowLayout
}
}
다른 모든 사용자가 이미 언급한 내용 외에도 UICollectionViewController를 하위 분류하는 대신 직접 CollectionView를 작성하는 경우 다음을 설정하는 것을 잊지 마십시오.
collectionView.translatesAutoresizingMaskIntoConstraints = false
제약 조건을 설정할 때.
사용해 보십시오.
class CollectionViewFlowLayout: UICollectionViewFlowLayout {
override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
if let collectionView = collectionView {
context.invalidateFlowLayoutDelegateMetrics = collectionView.bounds.size != newBounds.size
}
return context
}
}
설명서:
이 속성의 기본값은 false입니다.항목의 크기가 변경되어 레이아웃을 무효화하는 경우 이 속성을 true로 설정합니다.이 속성을 true로 설정하면 흐름 레이아웃 개체가 항목 및 보기의 크기를 다시 계산하여 필요에 따라 위임 개체를 쿼리합니다.
언급URL : https://stackoverflow.com/questions/36884376/swift-how-to-refresh-uicollectionview-layout-after-rotation-of-the-device
'programing' 카테고리의 다른 글
node.js, 오류: 'express' 모듈을 찾을 수 없습니다. (0) | 2023.08.29 |
---|---|
CSS를 사용하여 요소 앞에 줄 바꿈을 삽입하는 방법 (0) | 2023.08.29 |
PHP 및 InnoDB 엔진을 사용하는 mysqli_rollback()의 문제 (0) | 2023.08.29 |
ADB 설치가 실패하고 INSTALL_FAILED_가 발생함테스트_전용 (0) | 2023.08.29 |
CSS @font-face는 Firefox와 함께 작동하지 않지만 Chrome 및 IE와 함께 작동합니다. (0) | 2023.08.29 |