与„;开始开发iOS应用程序“-升级到Swift 3后的指南:评级控制不可见

与„;开始开发iOS应用程序“-升级到Swift 3后的指南:评级控制不可见,swift,xcode,uikit,interface-builder,Swift,Xcode,Uikit,Interface Builder,随着Swift 3.0和Xcode 8.0的发布,我决定看看我最近根据苹果自己的指南构建的一个项目——你可以下载示例项目 转换为当前Swift语法并对项目进行一些小的调整后,代码编译无误,应用程序运行无误,除了RatingControl.Swift中定义的自定义控件不可见外。使用Interface Builder将视图移出堆栈视图将使其可见,但一旦添加约束,它将再次消失 这是一个bug还是代码需要进一步调整 为完整性起见:代码已自动转换,隐私照片库使用说明已添加到Info.plist,在save

随着Swift 3.0和Xcode 8.0的发布,我决定看看我最近根据苹果自己的指南构建的一个项目——你可以下载示例项目

转换为当前Swift语法并对项目进行一些小的调整后,代码编译无误,应用程序运行无误,除了
RatingControl.Swift
中定义的自定义控件不可见外。使用Interface Builder将视图移出堆栈视图将使其可见,但一旦添加约束,它将再次消失

这是一个bug还是代码需要进一步调整

为完整性起见:代码已自动转换,隐私照片库使用说明已添加到Info.plist,在savefines()loadfines()中删除了强制展开,在prepare(发件人:)中添加了额外的下载位于
MealViewController.swift

先谢谢你


界面生成器


比率控制

导入UIKit
类分级控件:UIView{
//马克:财产
var评级=0{
迪塞特{
setNeedsLayout()
}
}
变量比率按钮=[UIButton]()
变量间距=5
var stars=5
//标记:初始化
必需的初始化?(编码器aDecoder:NSCoder){
super.init(编码者:aDecoder)
让filledStarImage=UIImage(名为:“filledStar”)
让emptyStarImage=UIImage(名为:“emptyStar”)

对于uu in 0..来说,答案有点长,但我会尝试给出一个简短的版本:当您将旧故事板转换为使用初始设备大小的模板时,最新的Xcode中有一个新行为。当控制器点击
viewDidLoad
时,控制器主视图的子视图的帧仍然没有计算出eir初始帧的大小都是1000x1000。在Xcode 8.0中将它们转换为IB之前,这会使一些UI无法正常工作,尤其是当您需要知道按钮的高度以计算其角半径时。在许多情况下,只需调用
self.view.layoutifneed()
在修改这些视图之前可以解决问题,但本项目并非如此

我不打算对此做更多的阐述,因为坦率地说,我仍然习惯于这些问题——在我的项目中,我会逐案处理它们

该项目正在使用堆栈视图/某些自动布局/内容模式的组合,这导致了一种奇怪的行为:在您引用的控制器上,问题似乎是容器堆栈视图不知道分级控件的高度

我尝试了几件事:首先,我在分级控件中添加了44.0的高度约束。这使它看起来很像,但宽度是错误的-它与堆栈视图的宽度匹配,分级控件是左对齐的,而不是居中对齐的

最终起作用的是:

  • 在堆栈视图中添加容器视图并将其高度设置为44.0
  • 使分级控件成为该容器视图的子视图
  • 将宽度和高度约束添加到额定值控件(240.0 x 44.0)
  • 将分级控件水平和垂直居中于其superview上
需要注意的是:没有设置宽度约束也会导致一种奇怪的行为——由于某种原因,评级控件的宽度非常大

我不知道这是否是您问题的最终答案,但这似乎已经成功了。如果这有帮助,您可以找到。我为容器视图和分级控件添加了背景色/边框色,以实现更好的可视化

希望这有帮助

import UIKit

class RatingControl: UIView {
    // MARK: Properties

    var rating = 0 {
        didSet {
            setNeedsLayout()
        }
    }
    var ratingButtons = [UIButton]()
    var spacing = 5
    var stars = 5

    // MARK: Initialization

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

        let filledStarImage = UIImage(named: "filledStar")
        let emptyStarImage = UIImage(named: "emptyStar")

        for _ in 0..<5 {
            let button = UIButton()

            button.setImage(emptyStarImage, for: UIControlState())
            button.setImage(filledStarImage, for: .selected)
            button.setImage(filledStarImage, for: [.highlighted, .selected])

            button.adjustsImageWhenHighlighted = false

            button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(_:)), for: .touchDown)
            ratingButtons += [button]
            addSubview(button)
        }
    }

    override func layoutSubviews() {
        // Set the button's width and height to a square the size of the frame's height.
        let buttonSize = Int(frame.size.height)
        var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)

        // Offset each button's origin by the length of the button plus spacing.
        for (index, button) in ratingButtons.enumerated() {
            buttonFrame.origin.x = CGFloat(index * (buttonSize + spacing))
            button.frame = buttonFrame
        }
        updateButtonSelectionStates()
    }

    override var intrinsicContentSize : CGSize {
        let buttonSize = Int(frame.size.height)
        let width = (buttonSize + spacing) * stars

        return CGSize(width: width, height: buttonSize)
    }

    // MARK: Button Action

    func ratingButtonTapped(_ button: UIButton) {
        rating = ratingButtons.index(of: button)! + 1

        updateButtonSelectionStates()
    }

    func updateButtonSelectionStates() {
        for (index, button) in ratingButtons.enumerated() {
            // If the index of a button is less than the rating, that button should be selected.
            button.isSelected = index < rating
        }
    }
}