仅在布置了UIView后才发送数据源信息吗? [英] Sending datasource information only when UIView has been laid out?

查看:94
本文介绍了仅在布置了UIView后才发送数据源信息吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难理解这一点.我早些时候已经能够做这样的项目,但是现在类似的方法对我不起作用.

I am having trouble understanding this. I have been able to do such a project earlier on but now a similar approach isn't working for me.

这是问题所在: 我有一个具有4 storyViewControllers的UIPageViewController. 每个storyViewControllers都有一个自定义的containerView.我想在containerView上添加UIViews的n个数字.我正在从视图控制器发送dataSource或'n'.但是,我不能正确地将子视图放在容器视图上.

Here is the issue: I have a UIPageViewController that has 4 storyViewControllers. Each storyViewControllers has a custom containerView. I want to add 'n' number of UIViews on the containerView. I am sending the dataSource or 'n' from the view controller. However, I am not able to correctly lay out the subviews on the container view.

基本上,我想知道何时从视图控制器发送数据源信息.显然,一旦添加了自定义容器视图,我就想发送它.

Essentially I want to know when to send the datasource info from the view controller. Obviously I would like to send it once the custom container view has been added.

我正在使用viewDidLayoutSubviews.这样就可以了.但是,我认为这不是正确的方法.现在,每次视图控制器布置子视图时,都会调用我的委托.

I am using viewDidLayoutSubviews. This makes it work. However, I don't think it's the correct way. Now every time the view controller lays out subviews my delegate will be called.

我尝试在viewDidLoad()中执行此操作,但这也不起作用.

I have tried doing it in viewDidLoad() but that doesn't work either.

这有效.但是似乎并不正确.阿尔斯 我的storyViewController代码

This works. But just doesn't seem right. Als My storyViewController code

    override func viewDidLoad() {
    super.viewDidLoad()
    segmentContainerView = ATCStorySegmentsView()
    view.addSubview(segmentContainerView)
    configureSegmentContainerView()
    segmentContainerView.translatesAutoresizingMaskIntoConstraints = false

}

  override func viewDidLayoutSubviews() {
    segmentContainerView.delegate = self
     DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.segmentContainerView.startAnimation() // I am also animating these views that are laid on the containerView. Doing them here starts the animation randomly whenever I scroll through the UIPageController
    }
}

containerView中:

    var delegate: ATCSegmentDataSource? {
    didSet {
        addSegments()
    }
}

private func addSegments() {
    let numberOfSegment = delegate?.numberOfSegmentsToShow()
    guard let segmentQuantity = numberOfSegment else { return }
    layoutIfNeeded()
    setNeedsLayout()
    for i in 0..<segmentQuantity {
        let segment = Segment()
        addSubview(segment.bottomSegment)
        addSubview(segment.topSegment)
        configureSegmentFrame(index: i, segmentView: segment)
        segmentsArray.append(segment)
    }


}

private func configureSegmentFrame(index: Int, segmentView: Segment) {
    let numberOfSegment = delegate?.numberOfSegmentsToShow()
    guard let segmentQuantity = numberOfSegment else { return }

    let widthOfSegment : CGFloat = (self.frame.width - (padding * CGFloat(segmentQuantity - 1))) / CGFloat(segmentQuantity)

    let i = CGFloat(index)

    let segmentFrame = CGRect(x: i * (widthOfSegment + padding), y: 0, width: widthOfSegment, height: self.frame.height)
    segmentView.bottomSegment.frame = segmentFrame
    segmentView.topSegment.frame = segmentFrame
    segmentView.topSegment.frame.size.width = 0

}

这按应有的方式工作.但是,当我滚动UIPageViewController时,动画并不总是从头开始.由于它依赖于布局子视图.有时,如果我在页面控制器中缓慢滚动,则子视图会重新布置,动画从头开始.其他时候,当视图已经加载时,动画从我遗漏的地方开始.

This works the way it should. But when I scroll the UIPageViewController, the animations don't always start from the beginning. Since it relies on laying out subviews. Sometimes if I slowly scroll through the page controller then the subviews are laid out again and the animation starts from start. Other times when the views are already loaded, the animations starts from where I left out.

问题,我想知道从视图控制器向containerView发送数据源的最佳方法是什么?该数据源是生成要添加到containerView上的视图数量所需要的.

Question I want to know what is the best way to send datasource from view controller to the containerView? That datasource is what will be needed to generate the amount of views to be added on the containerView.

这是我从viewDidLayoutSubviews发送数据源时得到的结果.我在今天早些时候提出了另一个问题,其中列出了我用来发送数据源的其他方法.还要看一下:

This is the result I get, if I send the datasource from viewDidLayoutSubviews. I have asked another question earlier today that lists other methods I used to send datasource. Take a look at that as well: Not able to lay out Subviews on a custom UIView in Swift

推荐答案

这是一个非常基本的示例...

This is a very basic example...

它具有:

  • 一个Segment: UIView子类,其中包含一个"bottomSegment"和一个"topSegment"
  • 具有UIStackViewATCStorySegmentsView: UIView子类,以布置所需的段数
  • 一个StoryViewController: UIViewController子类,在其视图的顶部添加了ATCStorySegmentsView,然后告诉该视图为viewDidAppear()上的第一段设置动画.
  • a Segment: UIView sub-class that holds a "bottomSegment" and a "topSegment"
  • a ATCStorySegmentsView: UIView sub-class with a UIStackView to layout the desired number of segments
  • a StoryViewController: UIViewController sub-class that adds ATCStorySegmentsView at the top of its view, and then tells that view to animate the first segment on viewDidAppear()
class Segment: UIView {

    let topSegment: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .white
        return v
    }()

    let bottomSegment: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .gray
        return v
    }()

    var startConstraint: NSLayoutConstraint = NSLayoutConstraint()
    var endConstraint: NSLayoutConstraint = NSLayoutConstraint()

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

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

    func commonInit() -> Void {
        addSubview(bottomSegment)
        addSubview(topSegment)

        // start constraint has width of Zero
        startConstraint = topSegment.widthAnchor.constraint(equalTo: bottomSegment.widthAnchor, multiplier: 0.0)

        // end constraint has width of bottomSegment
        endConstraint = topSegment.widthAnchor.constraint(equalTo: bottomSegment.widthAnchor, multiplier: 1.0)

        NSLayoutConstraint.activate([

            // bottomSegment constrained to all 4 sides
            bottomSegment.topAnchor.constraint(equalTo: topAnchor),
            bottomSegment.bottomAnchor.constraint(equalTo: bottomAnchor),
            bottomSegment.leadingAnchor.constraint(equalTo: leadingAnchor),
            bottomSegment.trailingAnchor.constraint(equalTo: trailingAnchor),

            // topSegment constrained top, bottom and leading
            topSegment.topAnchor.constraint(equalTo: topAnchor),
            topSegment.bottomAnchor.constraint(equalTo: bottomAnchor),
            topSegment.leadingAnchor.constraint(equalTo: leadingAnchor),

            // activate topSegemnt width constraint
            startConstraint,

            ])
    }

    func showTopSegment() -> Void {
        // deactivate startConstraint
        startConstraint.isActive = false
        // activate endConstraint
        endConstraint.isActive = true
    }

}

protocol ATCSegmentDataSource {
    func numberOfSegmentsToShow() -> Int
}

class ATCStorySegmentsView: UIView {

    let theStackView: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .horizontal
        v.alignment = .fill
        v.distribution = .fillEqually
        v.spacing = 4
        return v
    }()

    var segmentsArray: [Segment] = [Segment]()

    var delegate: ATCSegmentDataSource? {
        didSet {
            addSegments()
        }
    }

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

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

    func commonInit() -> Void {
        addSubview(theStackView)

        NSLayoutConstraint.activate([

            // constrain stack view to all 4 sides
            theStackView.topAnchor.constraint(equalTo: topAnchor),
            theStackView.bottomAnchor.constraint(equalTo: bottomAnchor),
            theStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
            theStackView.trailingAnchor.constraint(equalTo: trailingAnchor),

            ])
    }

    private func addSegments() {
        let numberOfSegment = delegate?.numberOfSegmentsToShow()
        guard let segmentQuantity = numberOfSegment else { return }

        // add desired number of Segment subviews to teh stack view
        for _ in 0..<segmentQuantity {
            let seg = Segment()
            seg.translatesAutoresizingMaskIntoConstraints = false
            theStackView.addArrangedSubview(seg)
            segmentsArray.append(seg)
        }
    }

    func startAnimation() -> Void {

        // this will animate changing the topSegment's width
        self.segmentsArray.first?.showTopSegment()
        UIView.animate(withDuration: 1.5, animations: {
            self.layoutIfNeeded()
        })

    }


}

class StoryViewController: UIViewController, ATCSegmentDataSource {

    let segmentContainerView: ATCStorySegmentsView = ATCStorySegmentsView()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .red

        view.addSubview(segmentContainerView)

        segmentContainerView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([

            // constrain segmentContainerView to top (safe-area) + 20-pts "padding",
            segmentContainerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),

            // leading and trailing with "padding" of 20-pts
            segmentContainerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
            segmentContainerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),

            // constrain height to 10-pts
            segmentContainerView.heightAnchor.constraint(equalToConstant: 10.0),

            ])

        // set the delegate
        segmentContainerView.delegate = self

    }

    override func viewDidAppear(_ animated: Bool) {
        segmentContainerView.startAnimation()
    }

    func numberOfSegmentsToShow() -> Int {
        return 3
    }

}


结果(显示视图时,白色条会在灰色条上动起来):


Result (the white bar animates across the gray bar when the view appears):

有5个细分:

请注意,正如自动布局的设计目的一样,它还可以处理尺寸变化,例如设备旋转时:

Note that, as auto-layout is designed to do, it also handles size-changes, such as when the device is rotated:

这篇关于仅在布置了UIView后才发送数据源信息吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆