Swift:自定义视图层次结构

时间:2017-10-10 12:04:41

标签: ios swift

我正在尝试建立一个电影票务应用程序,我需要帮助我的视图层次结构。目前我的cinemaView没有加入VC。

以下是我尝试这样做的方式。我有一个自定义seatView有两个属性(isVacant和seatNumber)和一个自定义cinemaView有[seatView]作为属性(不同的电影院有不同的座位)。我的代码是这样的:

//In my viewController
class ViewController: UIViewController, UIScrollViewDelegate {
    let scrollView = UIScrollView()
    let cinema: CinemaView = {
       let v = CinemaView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 10.0
        scrollView.zoomScale = scrollView.minimumZoomScale
        scrollView.delegate = self
        scrollView.isScrollEnabled = true
        scrollView.translatesAutoresizingMaskIntoConstraints = false

        let seat1 = SeatView()
        seat1.isVacant = false
        seat1.seatNumber = "2A"

        let seat2 = SeatView()
        seat2.isVacant = true
        seat2.seatNumber = "3B"
        cinema.seats = [seat1, seat2]

        view.addSubview(scrollView)
        scrollView.addSubview(cinema)

        let views = ["scrollView": scrollView, "v": cinema]
        let screenHeight = view.frame.height

        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[scrollView(\(screenHeight / 2))]", options: NSLayoutFormatOptions(), metrics: nil, views: views))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[scrollView]|", options: NSLayoutFormatOptions(), metrics: nil, views: views))

        scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-60-[v(50)]", options: NSLayoutFormatOptions(), metrics: nil, views: views))
        scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v]|", options: NSLayoutFormatOptions(), metrics: nil, views: views))

    }

//At my custom cinemaView
class CinemaView: UIView {

    var seats = [SeatView]()
    var xPos: Int = 0

    let cinemaView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

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

        addSubview(cinemaView)
        cinemaView.backgroundColor = .black

        let views = ["v": cinemaView]
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[v]|", options: NSLayoutFormatOptions(), metrics: nil, views: views))
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v]|", options: NSLayoutFormatOptions(), metrics: nil, views: views))


        for seat in seats {
            cinemaView.addSubview(seat)
            seat.frame = CGRect(x: xPos, y: 0, width: 20, height: 20)
            xPos += 8 
        }

    }

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

//At my custom seatView
class SeatView: UIView {
    var isVacant: Bool?
    var seatNumber: String?

    let seatView: UIView = {
        let v = UIView()
        v.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
        v.layer.cornerRadius = 5
        return v
    }()

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

        addSubview(seatView)
        seatView.backgroundColor = setupBackgroundColor()

    }

    func setupBackgroundColor() -> UIColor {
        if let isVacant = isVacant {
            if isVacant {
                return UIColor.green
            } else {
                return UIColor.black
            }
        } else {
            return UIColor.yellow
        }
    }

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

我的代码似乎没有将cinemaView添加到我的VC中。谁能指出我哪里出错了?或者甚至建议这种方法适合这种应用吗?感谢。

2 个答案:

答案 0 :(得分:1)

您需要在创建任何frames时提供UIView

当您指定自定义override init(frame: CGRect)的框架时,将调用

UIView

我创建了一个与您相似的层次结构作为示例。看看吧。

同样xPos必须与之前的SeatView不重叠,即(new xPos + previous SeatView width)

同样在您的SeatViewCinemaView中,您在另一个UIView内添加UIView,这是多余的。你不需要这样做。

示例:

class ViewController: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()

        let seat1 = SeatView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))

        let seat2 = SeatView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))

        let cinema = CinemaView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
        cinema.seats = [seat1, seat2]

        self.view.addSubview(cinema)
    }
}

class CinemaView: UIView
{
    var seats = [SeatView](){
        didSet{
            for seat in seats
            {
                seat.frame.origin.x = xPos
                xPos += 100
                self.addSubview(seat)
            }
        }
    }
    var xPos: CGFloat = 0

    override init(frame: CGRect)
    {
        super.init(frame: frame)
        self.backgroundColor = .black
    }

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

class SeatView: UIView
{
    override init(frame: CGRect)
    {
        super.init(frame: frame)
        self.backgroundColor = .red
    }

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

答案 1 :(得分:0)

嗯,你这里做错了几件事......

  1. 您正在为UIView创建子类,但在自定义视图中,您要添加UIView作为子视图,并尝试将其视为您的实际视图。

  2. 在你的CinemaView课程中,你正在循环你的“座位”阵列......但你在分配阵列之前正在这样做所以没有座位会是创建

  3. 同样,在SeatView中,您尝试根据.isVacant属性设置背景颜色,但在设置属性之前这样做。

  4. 您正在尝试使用UIScrollView,但没有正确设置约束。

  5. 使用可视化格式设置约束可能会感觉方便或简单,但它有限制。在您的特定情况下,您希望滚动视图是主视图高度的1/2。使用VFL,您必须计算并显式设置常量,这也必须在帧更改时重新计算。使用滚动视图.heightAnchor.constraint,将其设置为等于视图的.heightAnchor,设置multiplier: 0.5可获得50%而无需任何计算。

  6. 所以,要理解很多,要考虑很多。我对您的原始代码进行了一些修改。这应该被视为您学习的“起点”,而不是“插入完成的代码”:

    class CinemaViewController: UIViewController, UIScrollViewDelegate {
    
        let scrollView: UIScrollView = {
            let sv = UIScrollView()
            sv.minimumZoomScale = 1.0
            sv.maximumZoomScale = 10.0
            sv.zoomScale = sv.minimumZoomScale
            sv.isScrollEnabled = true
            sv.translatesAutoresizingMaskIntoConstraints = false
            return sv
        }()
    
        let cinema: CinemaView = {
            let v = CinemaView()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.backgroundColor = UIColor.black
            return v
        }()
    
        override func viewDidLoad() {
    
            super.viewDidLoad()
    
            // set our background to yellow, so we can see where it is
            view.backgroundColor = .yellow
    
            // create 2 "SeatView" objects
            let seat1 = SeatView()
            seat1.isVacant = false
            seat1.seatNumber = "2A"
    
            let seat2 = SeatView()
            seat2.isVacant = true
            seat2.seatNumber = "3B"
    
            // set the array of "seats" in the cinema view object
            cinema.seats = [seat1, seat2]
    
            // assign scroll view delegate
            scrollView.delegate = self
    
            // set scroll view background color, so we can see it
            scrollView.backgroundColor = .blue
    
            // add the scroll view to this view
            view.addSubview(scrollView)
    
            // pin scrollView to top, leading and trailing, with 8-pt padding
            scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true
    
            // set scrollView height to 50% of view height
            scrollView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5).isActive = true
    
            // add cinema view to the scroll view
            scrollView.addSubview(cinema)
    
            // pin cinema to top and leading of scrollView, with 8.0-pt padding
            cinema.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 8.0).isActive = true
            cinema.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 8.0).isActive = true
    
            // set the width of cinema to scrollView width -16.0 (leaves 8.0-pts on each side)
            cinema.widthAnchor.constraint(equalTo: scrollView.widthAnchor, constant: -16.0).isActive = true
    
            // cinema height set to constant of 60.0 (for now)
            cinema.heightAnchor.constraint(equalToConstant: 60.0).isActive = true
    
            // in order to use a scroll view, its .contentSize must be defined
            // so, use the trailing and bottom anchor constraints of cinema to define the .contentSize
            cinema.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0.0).isActive = true
            cinema.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0).isActive = true
    
        }
    
    }
    
    //At my custom cinemaView
    class CinemaView: UIView {
    
        // when the seats property is set,
        //  remove any existing seat views
        //  and add a new seat view for each seat
        // Note: eventually, this will likely be done with Stack Views and / or constraints
        //       rather than calculated Rects
        var seats = [SeatView]() {
            didSet {
                for v in self.subviews {
                    v.removeFromSuperview()
                }
                var seatRect = CGRect(x: 0, y: 0, width: 20, height: 20)
                for seat in seats {
                    self.addSubview(seat)
                    seat.frame = seatRect
                    seatRect.origin.x += seatRect.size.width + 8
                }
            }
        }
    
        // we're not doing anything on init() - yet...
        //  un-comment the following if you need to add setup code
        //  see the similar functionality in SeatView class
        /*
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
        */
    
        // perform any common setup tasks here
        func commonInit() -> Void {
            //
        }
    
    }
    
    //At my custom seatView
    class SeatView: UIView {
    
        // change the background color when .isVacant property is set
        var isVacant: Bool = false {
            didSet {
                if isVacant {
                    self.backgroundColor = UIColor.green
                } else {
                    self.backgroundColor = UIColor.red
                }
            }
        }
    
        // not used currently
        var seatNumber: String?
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
    
        // perform any common setup tasks here
        func commonInit() -> Void {
            self.layer.cornerRadius = 5
        }
    
    }