Ios Swift:自定义视图层次结构

Ios Swift:自定义视图层次结构,ios,swift,Ios,Swift,我正在尝试建立一个电影票务应用程序,我需要帮助我的视图层次结构。目前,我的cinemaView没有添加到VC中 下面是我如何尝试去做的。我有一个自定义seatView,它有两个属性(IsEmptable和seatNumber),还有一个自定义cinemaView,它有[seatView]作为属性(不同的电影院有不同的座位)。我的代码如下: //In my viewController class ViewController: UIViewController, UIScrollViewDele

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

下面是我如何尝试去做的。我有一个自定义seatView,它有两个属性(IsEmptable和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中。谁能告诉我哪里出了问题?或者,如果此方法适用于此应用程序,甚至可能提供建议?谢谢。

您需要在创建任何
ui视图时提供

override init(frame:CGRect)
将在指定自定义
UIView
的帧时调用

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

而且
xpo
必须确保其不会与先前的
SeatView
重叠,即
(新xpo+先前的SeatView宽度)

另外,在您的
SeatView
CinemaView
中,您正在另一个
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")
    }
}

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

  • 您正在子类化
    UIView
    ,但在自定义视图中,您正在将
    UIView
    添加为子视图,并尝试将其视为实际视图

  • 在你的
    CinemaView
    课程中,你正在循环你的“座位”数组。。。但是您在分配数组之前执行此操作,因此不会创建任何座位

  • 类似地,在
    SeatView
    中,您试图基于
    .isemptain
    属性设置背景色,但您是在设置属性之前进行的

  • 您正试图使用
    UIScrollView
    ,但未正确设置约束

  • 使用可视格式设置约束可能感觉方便或容易,但它有局限性。在特定情况下,您希望滚动视图的高度为主视图高度的1/2。使用VFL,您必须计算并显式设置常量,在帧更改时也必须重新计算该常量。使用滚动视图的
    .heightAnchor.constraint
    ,将其设置为视图的
    .heightAnchor
    ,并设置
    乘数:0.5
    ,无需任何计算即可获得50%

  • 所以,有很多需要理解和思考。我对你的原始代码做了一些编辑。这应该被视为您学习的“起点”,而不是“插入完成的代码”:


    我想,你好像没有把座位视图添加到电影院视图中。您已经在init方法中添加了座椅视图,并在之后进行了设置。因此,当您将SeatView设置为cinema view时,它只会添加属性,而不会添加视图。我想你应该创建一些函数,比如addSeats(),在里面,你可以在电影院视图中添加座椅视图,并重新验证这个视图。@Sunilluitel我想我在
    中为座椅中的座椅添加了
    ,也就是我调用了
    addSubview(seat)
    @Koh-你希望你的滚动视图是主视图高度的1/2吗?然后里面的“电影院”视图距离滚动视图的顶部60分,50分高,填充滚动视图的宽度?@DonMag yes。我希望cinemaView位于我的scrollView中。我的cinemaView的大小现在看起来很荒谬,因为我正在测试它,但是我会在以后更改cinemaView的大小以适应不同的电影院大小。谢谢你的解决方案。由于某种原因,
    cinemaView.backgroundColor
    确实被调用了。我尝试在其他地方调用它,比如在CinemaView和ViewController中,但没有发生。
    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
        }
    
    }