【问题标题】:About auto layouts关于自动布局
【发布时间】:2017-04-26 09:04:09
【问题描述】:

因为我已经为集合视图提供了自动布局,但纵向它很好:

问题在于,随着景观的发展,细胞之间的差距正在增加。如何避免这种情况?


import UIKit

class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
    @IBOutlet weak var collectionView: UICollectionView!
    var images = ["apple","banana","cherry","grapes","kiwifruit","mangoes","orange","papaya","passionfruit","peaches","pineapple","strawberry","sugarapple","watermelon"]

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged(notification:)), name: Notification.Name.UIDeviceOrientationDidChange, object: nil)
        collectionView.dataSource = self
        collectionView.delegate = self

    }
    func orientationChanged(notification: Notification) {
        collectionView.collectionViewLayout.invalidateLayout()
    }
        func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 1
        }

        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return images.count
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "stridentifier",for:indexPath) as! FruitsCollectionViewCell


            cell.image.image = UIImage(named: images[indexPath.row])
            cell.nameLabel.text = images[indexPath.row]

            return cell
        }
    class SampleCollectionViewFlowLayout: UICollectionViewFlowLayout {
        override init() {
            super.init()
            setUpLayout()
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setUpLayout()
        }

        override var itemSize: CGSize {
            set {}
            get {
                let itemWidth = ((self.collectionView?.bounds.width)! / 3.0) - self.minimumLineSpacing - minimumInteritemSpacing
                return CGSize(width: itemWidth, height: itemWidth)
            }
        }

        func setUpLayout() {
            minimumInteritemSpacing = 0
            minimumLineSpacing = 1.0
            scrollDirection = .vertical
        }

        override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
            return true 
        }
    }
}

【问题讨论】:

  • 你是使用 UICollectionViewFlowLayout 还是继承了它?
  • 你修复了 CollectionView 中的单元格大小吗?
  • 不,我没有使用 uicollectionviewflowlayout
  • 请详细解释您的问题,您尝试了什么,这是什么? UITableVIew,UICollectionView,UIScrollView?。如果您为自动布局编写了任何代码,请添加有问题的代码。

标签: ios swift swift3 autolayout


【解决方案1】:

如果你有 UICollectionViewFlowLayout 的子类,你需要覆盖 itemSize 并设置 minimumInteritemSpacingminimumLineSpacing 一旦你完成了,当方向改变时你需要告诉集合视图使用重置布局 collectionView.collectionViewLayout.invalidateLayout() 如果你没有子类化它,你需要重写

collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize

每当设备旋转时,调用 collectionView.collectionViewLayout.invalidateLayout() 您必须添加设备轮换通知。

这是一个 3 列的 UICollectionViewFlowLayout 示例

class SampleCollectionViewFlowLayout: UICollectionViewFlowLayout {
    override init() {
        super.init()
        setUpLayout()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUpLayout()
    }

    override var itemSize: CGSize {
        set {}
        get {
             // 2 columns in portrait and 3 in landscape
          if UIApplication.shared.statusBarOrientation.isPortrait {
           let itemWidth = ((self.collectionView?.bounds.width)! / 2.0) - self.minimumLineSpacing - minimumInteritemSpacing
           return CGSize(width: itemWidth, height: itemWidth)
         }
          let itemWidth = ((self.collectionView?.bounds.width)! / 3.0) - self.minimumLineSpacing - minimumInteritemSpacing
            return CGSize(width: itemWidth, height: itemWidth)
        }
    }

    func setUpLayout() {
        minimumInteritemSpacing = 0
        minimumLineSpacing = 1.0
        scrollDirection = .vertical
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true 
    }
}

viewDidLoad中添加这一行

NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged(notification:)), name: Notification.Name.UIDeviceOrientationDidChange, object: nil)

并添加功能

func orientationChanged(notification: Notification) {
  collectionView.collectionViewLayout.invalidateLayout()
}

另外,如果您使用 customLayout,您需要转到 xib->选择 collectionView->attributes 检查器(第 4 个选项)-> 点击 Layout 并选择 Custom 并将子类 UICollectionViewLayout 类的名称(SampleCollectionViewFlowLayout)放在我们的案例。

在 Storyboard/xib 文件中选择 UICollectionView 后,需要在此处设置 UICollectionViewFlowLayout 类

【讨论】:

  • 如何添加通知你能告诉我清楚我是新来的 swift
  • 你添加了定向方法,你能确认它的工作原理吗?
  • 您似乎在控制器中添加了 UICollectionViewFlowLayout 类。我在 github 上有我的示例,请检查此 link
  • 我尝试使用你的代码,但它没有工作可能是因为我也认为标签
  • 我的代码工作我已经测试过了。为 UICollectionViewLayout 创建一个新文件并确保在 xib 中设置它
【解决方案2】:

虽然我在这个线程中有点晚了。但我的解决方案将根据您的规范适用于所有设备。纵向 2 个单元格,横向 3 个单元格。但是您可以根据自己的选择增加单元格数量,只需修改 NUMBER_OF_CELLS_IN_PORTRAITNUMBER_OF_CELLS_IN_LANDSCAPE

我对每个函数都做了评论,所以可以理解。

起初我做了一些默认值。

let SCREEN_WIDTH = UIScreen.main.bounds.size.width
let SCREEN_HEIGHT = UIScreen.main.bounds.size.height

let BASE_SCREEN_HEIGHT:CGFloat = 736.0
let SCREEN_MAX_LENGTH = max(SCREEN_WIDTH, SCREEN_HEIGHT)
let ASPECT_RATIO_RESPECT_OF_7P = SCREEN_MAX_LENGTH / BASE_SCREEN_HEIGHT

let MINIMUM_INTERITEM_SPACING:CGFloat = 14.0 //My Default Inter Cell Spacing for iphone 7plus as i have designed in it.
var ITEM_WIDTH:CGFloat  = 200 //for iPhone 7 plus.initial value
var ITEM_HEIGHT:CGFloat = 200 //for iphone 7 plus.initial value
let NUMBER_OF_CELLS_IN_PORTRAIT:CGFloat = 2
let NUMBER_OF_CELLS_IN_LANDSCAPE:CGFloat = 3 

然后在 viewDidLoad 我用以下函数初始化集合视图

func initialCollectionData() {
    let orientation = UIApplication.shared.statusBarOrientation
    if orientation == .portrait {
        self.calculateCellSize(screenWidth:SCREEN_WIDTH ,cellItem: NUMBER_OF_CELLS_IN_PORTRAIT)
    }
    else {
        self.calculateCellSize(screenWidth:SCREEN_WIDTH ,cellItem: NUMBER_OF_CELLS_IN_LANDSCAPE)
    }
}

还有一些辅助功能。像这样。

//This method calculate cellsize according to cell number.
func  calculateCellSize(screenWidth:CGFloat , cellItem:CGFloat) {
    ITEM_WIDTH = (screenWidth - MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P  * (cellItem-1)) / cellItem  - 1// This 1 has been subtracted from ITEM_WIDTH to remove mantissa
    ITEM_HEIGHT = ITEM_WIDTH
}

//This method calculate cell number according to orientation.
func findCellItem(screenWidth:CGFloat)  {
    let orientation = UIDevice.current.orientation
    if orientation == .portrait {
        self.calculateCellSize(screenWidth:screenWidth ,cellItem: NUMBER_OF_CELLS_IN_PORTRAIT) //You have chosen 2 cells to show in portrait
    }
    else {
        self.calculateCellSize(screenWidth:screenWidth ,cellItem: NUMBER_OF_CELLS_IN_LANDSCAPE) ////You have chosen 3 cells to show in portrait
    }
}

//During orientation change this method is called everytime.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {

    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { context in

        context.viewController(forKey: UITransitionContextViewControllerKey.from)
        //Below two methods do the trick
        self.findCellItem(screenWidth: size.width)
        self.collectionView.collectionViewLayout.invalidateLayout()
    }, completion: {
        _ in
    })

}

//集合视图委托方法

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell

    collectionCell.fruitImage.image = UIImage.init(named: imageArray[indexPath.row])
    return collectionCell


}


 public func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}


public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize.init(width: ITEM_WIDTH , height: ITEM_HEIGHT )
}

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
   return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
}

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
}

【讨论】:

  • 但是让 SCREEN_MAX_LENGTH = max(SCREEN_WIDTH, SCREEN_HEIGHT) 让 ASPECT_RATIO_RESPECT_OF_7 = SCREEN_MAX_LENGTH / BASE_SCREEN_HEIGHT 代码不应该在那里 如果它在那里我们会得到一个错误稍后我把那个代码放在 func calculateCellSize 然后没有错误并且完美地工作
  • 你给自己打电话了吗? viewDidLoad 中的 initialCollectionData() 函数?
  • 现在我已经完成了,但是在横向模式下两列之间的空间正在增加我应该知道什么?
  • 老兄,我检查并仔细检查了所有设备,它在两种模式下都可以正常工作。
  • 但是横向的行间距更大,如何减少这个?
【解决方案3】:

只需在您的 ViewController 中添加以下行:

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
    self.yourCollectionView.collectionViewLayout.invalidateLayout()
}

并在此委托方法中设置适当的大小:

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

【讨论】:

  • 我否决了这个答案,因为它是在 2017 年发布的,其中 willRotateToInterfaceOrientation 方法已被 Apple 弃用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-11
  • 2013-09-23
相关资源
最近更新 更多