【问题标题】:add 2 image views to a uiscrollview func every time it is called每次调用 uiscrollview 函数时添加 2 个图像视图
【发布时间】:2020-08-04 10:50:24
【问题描述】:

我下面的快速代码目标是每次添加 2 个图像视图。你可以在下面的 gif 中只添加一个图像视图。我只需要添加 2 个图像视图。图像视图是 lastImage 和 lastImage2。您只能看到 lastImage 正在显示。看来我只能在调用 func didclickadd 时添加 1 个图像视图。

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()

    }

}

【问题讨论】:

    标签: swift uiscrollview imageview addsubview


    【解决方案1】:

    使用 Xcode 的“查看调试器”(按钮在我下面的屏幕快照中用红色圈起来),你会看到发生了什么:

    您的ss 视图没有背景颜色。请注意,当您创建该视图时,您不小心再次重置了 imgView 背景颜色,而不是设置了 ss.backgroundColor

    修复该问题,您将同时看到 imgViewss

    在尝试诊断此类问题时,视图调试器是您最好的朋友。现在,很明显,绿色视图可能不是您想要的,但您现在应该能够看到它并非常轻松地诊断该问题。


    所有这些都说了,一些观察:

    1. 你让生活变得比你需要的要困难得多。如果您只是为滚动视图和该滚动视图中的堆栈视图设置约束,那么您只需要添加一个排列的子视图。例如:

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

      注意,一旦设置了堆栈视图和滚动视图(见下文),那么您根本不需要处理contentSize 或这些子视图的约束(widthAnchor 和 @ 除外) 987654333@)。自动布局引擎,结合堆栈视图和滚动视图之间的约束,将为您处理一切。

      所以,一个完整的工作示例:

      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. 更好的是,我会在 Interface Builder 中亲自设置滚动视图、堆栈视图、按钮和所有相关的约束,然后我的示例中那个毛茸茸的 configure 方法就完全消失了。学习如何以编程方式创建视图很有趣,但在实际项目中,这很少是最高效的方式。在需要的地方执行编程视图(例如,通过单击按钮将排列的子视图添加到堆栈视图),否则,对于那些在您首次运行应用程序时应该存在的视图,值得考虑使用 Interface Builder。

      例如它大大减少了上面的代码量,只剩下:

      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 { ... }
      }
      

      显然,要习惯在 IB 中设计视图和配置约束需要一段时间,但值得付出努力。它将我们的代码提炼为最基本的要素。

    3. 在您的代码中,您为这些图像视图设置框架,然后设置translatesAutoresizingMaskIntoConstraints。在这种情况下设置frame 绝对没有意义,因为translatesAutoresizingMaskIntoConstraints 说“忽略我的frame,改用约束。”

    4. 我假设您这样做只是为了熟悉滚动视图,但值得注意的是,尤其是在添加大量图像视图时,滚动视图本质上是一种低效的方法。

      例如,假设您添加了 100 个图像视图,但一次只能看到 8 个。你真的想同时在内存中保存所有 100 个图像视图吗?没有。

      但是,UITableView,它是UIScrollView 的子类,负责处理这个问题。您最终只将当前可见的图像视图保留在内存中。这是一种更好的方法。

      当您开始使用实际的UIImage 对象时尤其如此,因为它们需要大量内存。看着大小合理的 PNG/JPG 资源,我们陷入了一种安全感,但是当它们被加载到内存中时,它们是未压缩的,并且需要不成比例的内存量。

    【讨论】:

      【解决方案2】:

      情侣笔记...

      通过适当的约束设置,自动布局会自行处理UIScrollView 的内容大小。无需设置scrollView.contentSize = ...

      您有几个将子视图(图像视图)添加到 mainView 的实例,这是您的滚动视图的子视图,但随后您将该子视图的约束添加到控制器的视图。确保将元素约束到适当的其他元素。

      这是您的代码,带有注释更改:

      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()
      
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-05
        • 2011-12-04
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 2013-01-20
        相关资源
        最近更新 更多