ios - UIStackView 在 iOS 12 中运行良好,但在 iOS 11 中运行不佳

标签 ios swift autolayout uistackview

在 iOS 12 中,我的水平堆栈 View (包含三个元素)布局得很好:

enter image description here

但在 iOS 11 中,这三个元素相互堆叠:

enter image description here

这是保存 Collection View 的 View Controller ,其中一行包含堆栈:

struct TestStackConstants {
    static let rowHeight:CGFloat = 200.0
    static let heightPerHoursLine:CGFloat = 16.0
}

public struct OpenHours: Equatable {
    public let instrument: String
    public let openStrings: [String] // ex: ["00:00 - 17:00", "17:15 - 24:00"]
    public let dayOfWeek: String // ex: Wednesday
    public let timeZone: String // ex: GMT-04:00
}

class TSViewController: UIViewController {

    fileprivate var extendedCollection: UICollectionView!
    fileprivate var myTimeZone = "GMT-0:400"
    fileprivate var dailyHours = [OpenHours]()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupView()

        let day1 = OpenHours(instrument: "USD/CAD", openStrings: ["00:00 - 12:00", "12:05 - 24:00"], dayOfWeek: "Monday", timeZone: "GMT-04:00")
        let day2 = OpenHours(instrument: "USD/CAD", openStrings: ["00:00 - 24:00"], dayOfWeek: "Tuesday", timeZone: "GMT-04:00")
        let day3 = OpenHours(instrument: "USD/CAD", openStrings: ["00:00 - 24:00"], dayOfWeek: "Wednesday", timeZone: "GMT-04:00")
        dailyHours = [day1, day2, day3]
    }

    private func setupView() {
        let myLayout = UICollectionViewFlowLayout()
        extendedCollection = UICollectionView(frame: .zero, collectionViewLayout: myLayout)
        extendedCollection.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(extendedCollection)
        NSLayoutConstraint.activate([
            extendedCollection.topAnchor.constraint(equalTo: self.view.topAnchor),
            extendedCollection.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            extendedCollection.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            extendedCollection.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
            ])
        extendedCollection.backgroundColor = .gray
        extendedCollection.dataSource = self
        extendedCollection.delegate = self
        extendedCollection.register(TSHoursInfoCell.self, forCellWithReuseIdentifier: "infoCell")
    }
}

// MARK: - Extensions

extension TSViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "infoCell", for: indexPath) as! TSHoursInfoCell
        cell.marketTitle.text = "MARKET HOURS (" + myTimeZone + ")"

        if self.dailyHours.count > 0 {
            for (item, hours) in zip(cell.threeDays, dailyHours) {
                item.dayOfWeek.text = hours.dayOfWeek.uppercased()
                item.hoursHeightConstraint.constant = CGFloat(hours.openStrings.count) * TestStackConstants.heightPerHoursLine
                item.dailyTime.text = hours.openStrings.joined(separator: "\n")
            }
        }

        return cell
    }
}

extension TSViewController: UICollectionViewDelegate { /*...*/ }

extension TSViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let returnSize = CGSize(width: collectionView.bounds.size.width, height: TestStackConstants.rowHeight)
        return returnSize
    }
}

这是集合单元格的定义,它包含标题和堆栈:

struct HoursConstants {
    static let insetting:CGFloat = 24.0
    static let titleSmallFont = UIFont.systemFont(ofSize: 12.0)
    static let titleSmallBoldFont = UIFont.boldSystemFont(ofSize: 12.0)
    static let hoursWidth:CGFloat = 96.0
}

class TSHoursInfoCell: UICollectionViewCell {
    public var marketTitle: UILabel!
    public var threeDays: [TSHoursDailyView]!

    fileprivate let horizStack: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.distribution = .fillEqually
        stackView.alignment = .center
        stackView.spacing = 0.0
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.backgroundColor = .gray

        marketTitle = UILabel(frame: .zero)
        marketTitle.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(marketTitle)
        NSLayoutConstraint.activate([
            marketTitle.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10.0),
            marketTitle.heightAnchor.constraint(equalToConstant: 20.0),
            marketTitle.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: HoursConstants.insetting),
            marketTitle.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -HoursConstants.insetting)
            ])
        marketTitle.font = HoursConstants.titleSmallBoldFont
        marketTitle.textColor = .orange
        marketTitle.textAlignment = .left

        contentView.addSubview(horizStack)
        NSLayoutConstraint.activate([
            horizStack.topAnchor.constraint(equalTo: marketTitle.bottomAnchor, constant: 7.0),
            horizStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: HoursConstants.insetting),
            horizStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -HoursConstants.insetting)
            ])

        threeDays = [TSHoursDailyView(), TSHoursDailyView(), TSHoursDailyView()]
        horizStack.addArrangedSubview(threeDays[0])
        horizStack.addArrangedSubview(threeDays[1])
        horizStack.addArrangedSubview(threeDays[2])
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class TSHoursDailyView: UIView {
    public var dayOfWeek: UILabel!
    public var dailyTime: UILabel!
    public var hoursHeightConstraint: NSLayoutConstraint!

    override init(frame: CGRect) {
        super.init(frame: frame)

        dayOfWeek = UILabel(frame: .zero)
        dayOfWeek.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(dayOfWeek)
        NSLayoutConstraint.activate([
            dayOfWeek.topAnchor.constraint(equalTo: self.topAnchor, constant: 5.0),
            dayOfWeek.heightAnchor.constraint(equalToConstant: 16.0),
            dayOfWeek.widthAnchor.constraint(equalToConstant: HoursConstants.hoursWidth)
            ])
        dayOfWeek.font = HoursConstants.titleSmallBoldFont
        dayOfWeek.textColor = .white
        dayOfWeek.textAlignment = .left

        dailyTime = UILabel(frame: .zero)
        dailyTime.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(dailyTime)
        NSLayoutConstraint.activate([
            dailyTime.topAnchor.constraint(equalTo: dayOfWeek.bottomAnchor, constant: 5.0),
            dailyTime.widthAnchor.constraint(equalToConstant: HoursConstants.hoursWidth)
            ])
        hoursHeightConstraint = dailyTime.heightAnchor.constraint(equalToConstant: 24.0)
        hoursHeightConstraint.isActive = true

        dailyTime.font = HoursConstants.titleSmallFont
        dailyTime.numberOfLines = 0
        dailyTime.textColor = .white
        dailyTime.textAlignment = .left
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

我没有看到任何特定于 iOS 12 的内容;为什么它们的布局不同?

最佳答案

我很惊讶你的代码在 iOS 12+ 上产生了这样的结果......但是,它确实做到了。

主要问题是缺少 dayOfWeekdailyTime 标签的 x 位置约束。

我做了一些其他可能有帮助的更改...而不是使用硬编码/计算的 hoursHeightConstraint.constant 添加了底部约束,以便它使用该标签的固有高度。

这是 12+ 的结果

enter image description here

11 日

enter image description here

和修改后的代码(只需查找 //DonMag: ... 注释 - 只有少数,但将整个内容发布回'cha 更可靠:)

struct TestStackConstants {
    static let rowHeight:CGFloat = 200.0
    static let heightPerHoursLine:CGFloat = 16.0
}

public struct OpenHours: Equatable {
    public let instrument: String
    public let openStrings: [String] // ex: ["00:00 - 17:00", "17:15 - 24:00"]
    public let dayOfWeek: String // ex: Wednesday
    public let timeZone: String // ex: GMT-04:00
}

class TSViewController: UIViewController {

    fileprivate var extendedCollection: UICollectionView!
    fileprivate var myTimeZone = "GMT-0:400"
    fileprivate var dailyHours = [OpenHours]()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupView()

        let day1 = OpenHours(instrument: "USD/CAD", openStrings: ["00:00 - 12:00", "12:05 - 24:00"], dayOfWeek: "Monday", timeZone: "GMT-04:00")
        let day2 = OpenHours(instrument: "USD/CAD", openStrings: ["00:00 - 24:00"], dayOfWeek: "Tuesday", timeZone: "GMT-04:00")
        let day3 = OpenHours(instrument: "USD/CAD", openStrings: ["00:00 - 24:00"], dayOfWeek: "Wednesday", timeZone: "GMT-04:00")
        dailyHours = [day1, day2, day3]
    }

    private func setupView() {
        let myLayout = UICollectionViewFlowLayout()
        extendedCollection = UICollectionView(frame: .zero, collectionViewLayout: myLayout)
        extendedCollection.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(extendedCollection)
        NSLayoutConstraint.activate([
            extendedCollection.topAnchor.constraint(equalTo: self.view.topAnchor),
            extendedCollection.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            extendedCollection.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            extendedCollection.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
            ])
        extendedCollection.backgroundColor = .gray
        extendedCollection.dataSource = self
        extendedCollection.delegate = self
        extendedCollection.register(TSHoursInfoCell.self, forCellWithReuseIdentifier: "infoCell")
    }
}

// MARK: - Extensions

extension TSViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "infoCell", for: indexPath) as! TSHoursInfoCell
        cell.marketTitle.text = "MARKET HOURS (" + myTimeZone + ")"

        if self.dailyHours.count > 0 {
            for (item, hours) in zip(cell.threeDays, dailyHours) {
                item.dayOfWeek.text = hours.dayOfWeek.uppercased()

                // DonMag: remove -- using label intrinsic height
//              item.hoursHeightConstraint.constant = CGFloat(hours.openStrings.count) * TestStackConstants.heightPerHoursLine

                item.dailyTime.text = hours.openStrings.joined(separator: "\n")
            }
        }

        return cell
    }
}

extension TSViewController: UICollectionViewDelegate { /*...*/ }

extension TSViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let returnSize = CGSize(width: collectionView.bounds.size.width, height: TestStackConstants.rowHeight)
        return returnSize
    }
}

struct HoursConstants {
    static let insetting:CGFloat = 24.0
    static let titleSmallFont = UIFont.systemFont(ofSize: 12.0)
    static let titleSmallBoldFont = UIFont.boldSystemFont(ofSize: 12.0)
    static let hoursWidth:CGFloat = 96.0
}

class TSHoursInfoCell: UICollectionViewCell {
    public var marketTitle: UILabel!
    public var threeDays: [TSHoursDailyView]!

    fileprivate let horizStack: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.distribution = .fillEqually

        // DonMag: changed alignment
        stackView.alignment = .top  // .center

        stackView.spacing = 0.0
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.backgroundColor = .gray

        marketTitle = UILabel(frame: .zero)
        marketTitle.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(marketTitle)
        NSLayoutConstraint.activate([
            marketTitle.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10.0),
            marketTitle.heightAnchor.constraint(equalToConstant: 20.0),
            marketTitle.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: HoursConstants.insetting),
            marketTitle.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -HoursConstants.insetting)
            ])
        marketTitle.font = HoursConstants.titleSmallBoldFont
        marketTitle.textColor = .orange
        marketTitle.textAlignment = .left

        contentView.addSubview(horizStack)
        NSLayoutConstraint.activate([
            horizStack.topAnchor.constraint(equalTo: marketTitle.bottomAnchor, constant: 7.0),
            horizStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: HoursConstants.insetting),
            horizStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -HoursConstants.insetting)
            ])

        threeDays = [TSHoursDailyView(), TSHoursDailyView(), TSHoursDailyView()]
        horizStack.addArrangedSubview(threeDays[0])
        horizStack.addArrangedSubview(threeDays[1])
        horizStack.addArrangedSubview(threeDays[2])

        // DonMag: un-comment to clearly see the view frames
//      threeDays[0].backgroundColor = .red
//      threeDays[1].backgroundColor = .blue
//      threeDays[2].backgroundColor = .brown

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class TSHoursDailyView: UIView {
    public var dayOfWeek: UILabel!
    public var dailyTime: UILabel!

    // DonMag: remove -- use label bottomAnchor to determine height
//  public var hoursHeightConstraint: NSLayoutConstraint!

    override init(frame: CGRect) {
        super.init(frame: frame)

        dayOfWeek = UILabel(frame: .zero)
        dayOfWeek.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(dayOfWeek)
        NSLayoutConstraint.activate([
            dayOfWeek.topAnchor.constraint(equalTo: self.topAnchor, constant: 5.0),
            dayOfWeek.heightAnchor.constraint(equalToConstant: 16.0),
            dayOfWeek.widthAnchor.constraint(equalToConstant: HoursConstants.hoursWidth),

            // DonMag: added -- needed x-position
            dayOfWeek.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0),

            ])
        dayOfWeek.font = HoursConstants.titleSmallBoldFont
        dayOfWeek.textColor = .white
        dayOfWeek.textAlignment = .left

        dailyTime = UILabel(frame: .zero)
        dailyTime.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(dailyTime)
        NSLayoutConstraint.activate([
            dailyTime.topAnchor.constraint(equalTo: dayOfWeek.bottomAnchor, constant: 5.0),
            dailyTime.widthAnchor.constraint(equalToConstant: HoursConstants.hoursWidth),

            // DonMag: added -- needed x-position
            dailyTime.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0),

            // DonMag: added -- use label height to determine height of view
            dailyTime.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: -5.0),

            ])

        // DonMag: remove -- use label bottomAnchor to determine height
//      hoursHeightConstraint = dailyTime.heightAnchor.constraint(equalToConstant: 24.0)
//      hoursHeightConstraint.isActive = true

        dailyTime.font = HoursConstants.titleSmallFont
        dailyTime.numberOfLines = 0
        dailyTime.textColor = .white
        dailyTime.textAlignment = .left
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

关于ios - UIStackView 在 iOS 12 中运行良好,但在 iOS 11 中运行不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56152553/

相关文章:

ios Metal : limit on number of variables used in a shader

ios - Swift:切片 startIndex 始终为 0

swift - 构建失败并显示 "Command CompileStoryboard failed with a nonzero exit code"

ios - View 根据设备屏幕大小更改大小,但应具有固定大小

ios - objective-c - ScrollView 内的多个 ScrollView

ios - 没有填充的画圈 IOS

ios - 我已经使用 cncontacts 获取联系人并能够在控制台屏幕上显示它们,但不能在 TableView 上显示

ios - 创建错误的 NSDate

ios - 在特定位置截断 UILabel

ios - 在 iphone 6/6 plus 上更改高度限制时移动 UIView