简体   繁体   English

每次调用 uiscrollview 函数时添加 2 个图像视图

[英]add 2 image views to a uiscrollview func every time it is called

My swift code below goal is to add 2 image views every time.我的目标下面的 swift 代码是每次添加 2 个图像视图。 Ass you can in the gif below only one image view is being added.你可以在下面的 gif 中只添加一个图像视图。 I just need to add 2 image views.我只需要添加 2 个图像视图。 The image views are lastImage and lastImage2.图像视图是 lastImage 和 lastImage2。 you can see only lastImage is being shown.您只能看到 lastImage 正在显示。 It seems I can only add 1 imageview when func didclickadd is called.看来我只能在调用 func didclickadd 时添加 1 个 imageview 。

在此处输入图像描述

import UIKit

class ViewController: UIViewController {

    fileprivate var  lastImage:UIImageView?
    fileprivate var  lastImage2:UIImageView?

    fileprivate var mainViewBootom:NSLayoutConstraint?


    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupVIew()
    }

    override func viewDidAppear(_ animated: Bool) {
        scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
        view.layoutIfNeeded()
    }

    //MARK: Components
    let scrollView:UIScrollView = {
        let sv = UIScrollView(frame: .zero)
        return sv
    }()

    let mainView:UIView = {
        let uv = UIView()
        uv.backgroundColor = .white
        return uv
    }()

    let btnAdd:UIButton = {
        let btn = UIButton(type: .system)
        btn.setTitle("Add", for: .normal)
        return btn
    }()



    let textField:UITextField = {
        let jake = UITextField()
        return jake

    }()

    //MARK: Setup UI
    func setupVIew() {
        view.addSubview(scrollView)
        view.addSubview(btnAdd)
        view.addSubview(textField)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        btnAdd.translatesAutoresizingMaskIntoConstraints = false
        textField.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([




            btnAdd.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            btnAdd.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
            btnAdd.widthAnchor.constraint(equalToConstant: 100),
            btnAdd.heightAnchor.constraint(equalToConstant: 45),


            //
            textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 25),
            textField.widthAnchor.constraint(equalToConstant: 100),
            textField.heightAnchor.constraint(equalToConstant: 45),
            //



            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: btnAdd.topAnchor , constant: -12),
        ])
        btnAdd.addTarget(self, action: #selector(didClickedAdd), for: .touchUpInside)

        scrollView.addSubview(mainView)
        mainView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            mainView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            mainView.topAnchor.constraint(equalTo: scrollView.topAnchor),
        ])

        let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 100))
        imgView.backgroundColor  = .red
        mainView.addSubview(imgView)


        let samsam = UIImageView(frame: CGRect(x: 0, y: 200, width: 40, height: 100))
        samsam.backgroundColor  = .blue
        mainView.addSubview(samsam)




        imgView.translatesAutoresizingMaskIntoConstraints = false
        imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imgView.widthAnchor.constraint(equalToConstant: 150).isActive = true
        imgView.heightAnchor.constraint(equalToConstant: 100).isActive = true


        samsam.translatesAutoresizingMaskIntoConstraints = false
        samsam.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        samsam.topAnchor.constraint(equalTo: imgView.bottomAnchor).isActive = true
        samsam.widthAnchor.constraint(equalToConstant: 75).isActive = true
        samsam.heightAnchor.constraint(equalToConstant: 100).isActive = true



        if lastImage != nil {
            imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 20).isActive = true
        }else{
            imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 12).isActive = true
        }
        lastImage = samsam
        mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 12)
        mainViewBootom!.isActive = true
    }

    @objc func didClickedAdd(){
        let imgView = UIImageView(frame: CGRect(x: 20, y: 0, width: 30, height: 20))
        imgView.backgroundColor  = .orange
        mainView.addSubview(imgView)

        let ss = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 50))
        imgView.backgroundColor  = .green
        mainView.addSubview(ss)


        imgView.translatesAutoresizingMaskIntoConstraints = false
        imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imgView.widthAnchor.constraint(equalToConstant: 40).isActive = true
        imgView.heightAnchor.constraint(equalToConstant: 60).isActive = true


        ss.translatesAutoresizingMaskIntoConstraints = false
        ss.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = false
        ss.widthAnchor.constraint(equalToConstant: 80).isActive = true
        ss.heightAnchor.constraint(equalToConstant: 90).isActive = true

        if lastImage != nil {

            ss.topAnchor.constraint(equalTo: imgView.topAnchor , constant: 20).isActive = true

            imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 50).isActive = true



        }else{
            imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 10).isActive = true
            ss.bottomAnchor.constraint(equalTo: imgView.bottomAnchor , constant: 25).isActive = true


        }


        lastImage = imgView
        lastImage2 = ss
        mainView.removeConstraint(mainViewBootom!)


        mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage2!.bottomAnchor , constant: 40)




        mainViewBootom!.isActive = true
        view.layoutIfNeeded()

        scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
        view.layoutIfNeeded()

    }

}

Couple notes...情侣笔记...

With proper constraint setup, auto-layout handles the UIScrollView content size all by itself.通过适当的约束设置,自动布局可以自行处理UIScrollView内容大小。 No need to ever set scrollView.contentSize =...无需设置scrollView.contentSize =...

You have several instances of adding a subview (image view) to your mainView , which is a subview of your scroll view, but then you add constraints from that subview to your controller's view.您有几个向mainView添加子视图(图像视图)的实例,这是滚动视图的子视图,但随后您将该子视图的约束添加到控制器的视图。 Make sure you are constraining elements to the proper other elements.确保将元素约束到适当的其他元素。

Here's your code, with commented changes:这是您的代码,带有注释更改:

class BenViewController: UIViewController {

    fileprivate var  lastImage:UIImageView?

// 1) don't need this
//  fileprivate var  lastImage2:UIImageView?

    fileprivate var mainViewBootom:NSLayoutConstraint?


    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupVIew()
    }

// 2) don't need this
//  override func viewDidAppear(_ animated: Bool) {
//      scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
//      view.layoutIfNeeded()
//  }

    //MARK: Components
    let scrollView:UIScrollView = {
        let sv = UIScrollView(frame: .zero)
        return sv
    }()

    let mainView:UIView = {
        let uv = UIView()
        uv.backgroundColor = .white
        return uv
    }()

    let btnAdd:UIButton = {
        let btn = UIButton(type: .system)
        btn.setTitle("Add", for: .normal)
        return btn
    }()



    let textField:UITextField = {
        let jake = UITextField()
        return jake

    }()

    //MARK: Setup UI
    func setupVIew() {
        view.addSubview(scrollView)
        view.addSubview(btnAdd)
        view.addSubview(textField)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        btnAdd.translatesAutoresizingMaskIntoConstraints = false
        textField.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([




            btnAdd.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            btnAdd.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
            btnAdd.widthAnchor.constraint(equalToConstant: 100),
            btnAdd.heightAnchor.constraint(equalToConstant: 45),


            //
            textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 25),
            textField.widthAnchor.constraint(equalToConstant: 100),
            textField.heightAnchor.constraint(equalToConstant: 45),
            //



            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: btnAdd.topAnchor , constant: -12),
        ])
        btnAdd.addTarget(self, action: #selector(didClickedAdd), for: .touchUpInside)

        scrollView.addSubview(mainView)
        mainView.translatesAutoresizingMaskIntoConstraints = false

// 3) change this:
//      NSLayoutConstraint.activate([
//          mainView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
//          mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
//          mainView.topAnchor.constraint(equalTo: scrollView.topAnchor),
//      ])
//

// to this
        NSLayoutConstraint.activate([
            mainView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            mainView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            mainView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            mainView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            mainView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
        ])
// end of change 3)

        let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 100))
        imgView.backgroundColor  = .red
        mainView.addSubview(imgView)


        let samsam = UIImageView(frame: CGRect(x: 0, y: 200, width: 40, height: 100))
        samsam.backgroundColor  = .blue
        mainView.addSubview(samsam)


        imgView.translatesAutoresizingMaskIntoConstraints = false

// 4) change view.centerXAnchor to mainView.centerXAnchor
//      imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imgView.centerXAnchor.constraint(equalTo: mainView.centerXAnchor).isActive = true

        imgView.widthAnchor.constraint(equalToConstant: 150).isActive = true
        imgView.heightAnchor.constraint(equalToConstant: 100).isActive = true


        samsam.translatesAutoresizingMaskIntoConstraints = false

// 5) change view.centerXAnchor to mainView.centerXAnchor
//      samsam.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        samsam.centerXAnchor.constraint(equalTo: mainView.centerXAnchor).isActive = true

        samsam.topAnchor.constraint(equalTo: imgView.bottomAnchor).isActive = true
        samsam.widthAnchor.constraint(equalToConstant: 75).isActive = true
        samsam.heightAnchor.constraint(equalToConstant: 100).isActive = true



        if lastImage != nil {
            imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 20).isActive = true
        }else{
            imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 12).isActive = true
        }
        lastImage = samsam
        mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 12)
        mainViewBootom!.isActive = true
    }

    @objc func didClickedAdd(){
        let imgView = UIImageView(frame: CGRect(x: 20, y: 0, width: 30, height: 20))
        imgView.backgroundColor  = .orange
        mainView.addSubview(imgView)

        let ss = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 50))

// 6) typo or copy/paste mistake
//      imgView.backgroundColor  = .green
        ss.backgroundColor  = .green

        mainView.addSubview(ss)


        imgView.translatesAutoresizingMaskIntoConstraints = false

// 7) change view.centerXAnchor to mainView.centerXAnchor
//      imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        imgView.centerXAnchor.constraint(equalTo: mainView.centerXAnchor).isActive = true

        imgView.widthAnchor.constraint(equalToConstant: 40).isActive = true
        imgView.heightAnchor.constraint(equalToConstant: 60).isActive = true


        ss.translatesAutoresizingMaskIntoConstraints = false

// 8) change view.leadingAnchor to mainView.leadingAnchor
//      ss.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = false
        ss.leadingAnchor.constraint(equalTo: mainView.leadingAnchor).isActive = false

        ss.widthAnchor.constraint(equalToConstant: 80).isActive = true
        ss.heightAnchor.constraint(equalToConstant: 90).isActive = true

// 9) always need to do this ... but did you mean imgView.bottomAnchor?
        ss.topAnchor.constraint(equalTo: imgView.topAnchor , constant: 20).isActive = true

        if lastImage != nil {

            // 9a) instead of only here
            //ss.topAnchor.constraint(equalTo: imgView.topAnchor , constant: 20).isActive = true

            imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 50).isActive = true

        }else{

            imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 10).isActive = true

        }

// 10) always need to do this
        // deactivate bottom constraint
        mainViewBootom?.isActive = false
        lastImage = ss
        mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage!.bottomAnchor, constant: 40)
        mainViewBootom?.isActive = true

// 11) don't need any of this
//      lastImage = imgView
//      lastImage2 = ss
//      mainView.removeConstraint(mainViewBootom!)
//
//
//      mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage2!.bottomAnchor , constant: 40)
//
//
//
//
//      mainViewBootom!.isActive = true
//      view.layoutIfNeeded()
//
//      scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
//      view.layoutIfNeeded()

    }

}

Use Xcode's “view debugger” (the button is circled in red in my screen snapshot below) and you'll see what's going on:使用 Xcode 的“查看调试器”(按钮在下面的屏幕快照中用红色圈出),你会看到发生了什么:

在此处输入图像描述

Your ss view has no background color.您的ss视图没有背景颜色。 Note, that when you created that view, you accidentally reset the imgView background color a second time rather than setting the ss.backgroundColor .请注意,当您创建该视图时,您不小心再次重置了imgView背景颜色,而不是设置ss.backgroundColor

Fix that and you'll see your both imgView and ss :修复它,你会看到你的imgViewss

在此处输入图像描述

The view debugger is your best friend when trying to diagnose issues like this.在尝试诊断此类问题时,视图调试器是您最好的朋友。 Now, obviously, the green view probably isn't where you intended it, but you should now be able to see it and diagnose that issue very easily.现在,很明显,绿色视图可能不是您想要的,但是您现在应该能够看到它并非常容易地诊断该问题。


All of this having been said, a few observations:所有这些都说了,一些观察:

  1. You're making life much harder than you need to.你让生活变得比你需要的要困难得多。 If you just set the constraints for the scroll view and a stack view within that scroll view, you then only need to add an arranged subview.如果您只是为滚动视图和该滚动视图中的堆栈视图设置约束,那么您只需要添加一个排列的子视图。 For example:例如:

     @objc func didTapButton(_ sender: UIButton) { stackView.addArrangedSubview(randomView()) stackView.addArrangedSubview(randomView()) }

    Note, once the stack view and scroll view have been set up (see below), then you don't need to mess around with contentSize or constraints for these subviews at all (other than the widthAnchor and heightAnchor ).请注意,一旦设置了堆栈视图和滚动视图(见下文),那么您根本不需要处理这些子视图的contentSize或约束(除了widthAnchorheightAnchor )。 The auto layout engine, combined with the constraints between the stack view and the scroll view, will take care of everything for you.自动布局引擎,结合堆栈视图和滚动视图之间的约束,将为您处理一切。

    So, a full working example:因此,一个完整的工作示例:

     class ViewController: UIViewController { let scrollView: UIScrollView = { let scrollView = UIScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false return scrollView }() let stackView: UIStackView = { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis =.vertical stackView.alignment =.center stackView.spacing = 10 return stackView }() let button: UIButton = { let button = UIButton(type: .system) button.translatesAutoresizingMaskIntoConstraints = false button.setTitle("Add", for: .normal) button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside) return button }() override func viewDidLoad() { super.viewDidLoad() configure() } } // MARK: - Actions extension ViewController { @objc func didTapButton(_ sender: UIButton) { stackView.addArrangedSubview(randomView()) stackView.addArrangedSubview(randomView()) } } // MARK: - Private utility methods private extension ViewController { func configure() { view.addSubview(scrollView) view.addSubview(button) scrollView.addSubview(stackView) NSLayoutConstraint.activate([ // define frame of `scrollView` scrollView.topAnchor.constraint(equalTo: view.topAnchor), scrollView.bottomAnchor.constraint(equalTo: button.topAnchor), scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), // define frame of `button` button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.bottomAnchor.constraint(equalTo: view.bottomAnchor), // define contentSize of `scrollView` based upon size of `stackView` stackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor), stackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor), stackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), // but define width of `stackView` relative to the _main view_ stackView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor) ]) button.setContentHuggingPriority(.required, for: .vertical) } func randomView() -> UIView { let widthRange = view.bounds.width * 0.1... view.bounds.width * 0.9 let heightRange = view.bounds.width * 0.1... view.bounds.width * 0.25 let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ view.widthAnchor.constraint(equalToConstant: .random(in: widthRange)), view.heightAnchor.constraint(equalToConstant: .random(in: heightRange)) ]) view.backgroundColor = UIColor(red: .random(in: 0.25...1), green: .random(in: 0.25...1), blue: .random(in: 0.25...1), alpha: 1) return view } }
  2. Even better, I'd personally set up the scroll view, stack view, button, and all the associated constraints in Interface Builder, and then that hairy configure method in my example goes away completely.更好的是,我会亲自在 Interface Builder 中设置滚动视图、堆栈视图、按钮和所有相关的约束,然后我的示例中的那个毛茸茸的configure方法就完全消失了。 It's fun to learn how to create views programmatically, but in real-world projects, it's rarely the most productive way to do it.学习如何以编程方式创建视图很有趣,但在实际项目中,这很少是最高效的方法。 Do programmatic views where needed (eg adding arranged subviews to the stack view on the click of a button), but otherwise, for those views that should be there when you first run the app, Interface Builder is worth considering.在需要的地方执行程序化视图(例如,通过单击按钮将排列的子视图添加到堆栈视图),否则,对于那些在您第一次运行应用程序时应该存在的视图,Interface Builder 值得考虑。

    Eg It dramatically reduces the amount of code above, leaving us simply with:例如,它大大减少了上面的代码量,只剩下:

     class ViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView: @IBOutlet weak var stackView: UIStackView. @IBAction func didTapButton(_ sender. UIButton) { stackView:addArrangedSubview(randomView()) stackView.addArrangedSubview(randomView()) } } // MARK. - Private utility methods private extension ViewController { func randomView() -> UIView {... } }

    Clearly, it takes a while to get used to designing views and configuring constraints in IB, but it's worth the effort.显然,习惯在 IB 中设计视图和配置约束需要一段时间,但值得付出努力。 It distills our code down the the bare essentials.它将我们的代码提炼成最基本的要素。

  3. In your code, you're setting frames for these image views and then setting translatesAutoresizingMaskIntoConstraints .在您的代码中,您为这些图像视图设置框架,然后设置translatesAutoresizingMaskIntoConstraints There's absolutely no point in setting the frame in that case, because translatesAutoresizingMaskIntoConstraints says “ignore my frame , use constraints instead.”在这种情况下设置frame绝对没有意义,因为translatesAutoresizingMaskIntoConstraints说“忽略我的frame ,改用约束”。

  4. I'm assuming you're doing all of this just to become familiar with scroll views, but it's worth noting that, especially when adding lots of image views, that the scroll view is an inherently inefficient approach.我假设您这样做只是为了熟悉滚动视图,但值得注意的是,特别是在添加大量图像视图时,滚动视图本质上是一种低效的方法。

    For example, let's say you've added 100 image views, but you can see only 8 at a time.例如,假设您添加了 100 个图像视图,但一次只能看到 8 个。 Do you really want to hold all 100 image views in memory at the same time?您真的想同时保存 memory 中的所有 100 个图像视图吗? No.不。

    But, UITableView , which is a subclass of UIScrollView , takes care of this.但是, UITableView ,它是UIScrollView的子类,负责这个。 You end up only keeping the currently visible image views in memory.您最终只会在 memory 中保留当前可见的图像视图。 It's a far better approach.这是一种更好的方法。

    This is especially true when you start using actual UIImage objects, because they require a lot of memory.当您开始使用实际UIImage对象时尤其如此,因为它们需要大量的 memory。 We get lulled into a sense of security, looking at reasonably sized PNG/JPG assets, but when they're loaded into memory, they're uncompressed and require a disproportionate amount of memory.看着大小合理的 PNG/JPG 资产,我们陷入了一种安全感,但是当它们被加载到 memory 时,它们是未压缩的,并且需要不成比例的 memory。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM