добавьте 2 просмотра изображений к забавному просмотру uiscrollview c каждый раз, когда он вызывается - PullRequest
0 голосов
/ 21 апреля 2020

Мой быстрый код ниже цели, чтобы добавить 2 просмотра изображений каждый раз. Задницу вы можете в GIF ниже только один вид изображения добавляется. Мне просто нужно добавить 2 изображения. Представления изображения являются lastImage и lastImage2. вы можете видеть только lastImage показывается. Кажется, я могу добавить только 1 просмотр изображения, когда вызывается забавный c didclickadd.

enter image description here

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

    }

}

Ответы [ 2 ]

0 голосов
/ 21 апреля 2020

Используйте «просмотр отладчика» Xcode (кнопка обведена красным на моем снимке экрана ниже), и вы увидите, что происходит:

enter image description here

Ваш ss вид не имеет цвета фона. Обратите внимание, что когда вы создали это представление, вы случайно сбросили фоновый цвет imgView во второй раз, вместо того, чтобы установить ss.backgroundColor.

Исправить это, и вы увидите свои imgView и ss:

enter image description here

Отладчик представлений - ваш лучший друг, когда вы пытаетесь диагностировать подобные проблемы. Теперь, очевидно, зеленый вид, вероятно, не там, где вы его предполагали, но теперь вы должны быть в состоянии увидеть его и очень легко диагностировать эту проблему.


Все это уже было сказано, несколько наблюдения:

  1. Вы делаете жизнь намного сложнее, чем нужно. Если вы просто устанавливаете ограничения для вида прокрутки и стека в этом представлении прокрутки, вам нужно только добавить упорядоченное подпредставление. Например:

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

    Обратите внимание, что после настройки стека и прокрутки (см. Ниже) вам больше не нужно возиться с contentSize или ограничениями для этих подпредставлений. (кроме widthAnchor и heightAnchor). Механизм автоматической разметки в сочетании с ограничениями между представлением стека и представлением прокрутки позаботится обо всем за вас.

    Итак, полный рабочий пример:

    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. Еще лучше, я лично настроил бы представление прокрутки, представление стека, кнопку и все связанные ограничения в Интерфейсном Разработчике, и тогда этот волосатый метод configure в моем примере полностью исчезнет. Интересно узнать, как создавать представления программно, но в реальных проектах это редко бывает наиболее продуктивным способом сделать это. Программируйте c представления там, где это необходимо (например, добавляя упорядоченные подпредставления в представление стека одним нажатием кнопки), но в противном случае для тех представлений, которые должны присутствовать при первом запуске приложения, 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 разумного размера, но когда они загружаются в память, они распаковываются и требуют непропорционального объема памяти.

0 голосов
/ 21 апреля 2020

Пара примечаний ...

При правильной настройке ограничений, автоматическая разметка обрабатывает размер контента 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()

    }

}
...