Проблема с макетом Swift ScrollView в PageController и изображениях iOS

#ios #swift #uiscrollview #scrollview #uipagecontrol

#iOS #swift #uiscrollview #scrollview #uipagecontrol

Вопрос:

Я не могу понять, как установить ограничения для ScrollView с ImageView внутри. Я использую ScrollView с помощью pageConroller для прокрутки множества изображений.

Смотрите мой макет на рисунке ниже.

// Код для ImageView

 for index in 0..<drinksImagesArray.count {
        frame.origin.x = scrollView.frame.size.width * CGFloat(index)
        frame.size = scrollView.frame.size
        
        let imageView = UIImageView(frame: frame)
        imageView.contentMode = .scaleAspectFit
        imageView.image = UIImage(named: imagesArray[index].name)
        self.scrollView.addSubview(imageView)
    }
    scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(imagesArray.count), height: scrollView.frame.size.height)
    scrollView.delegate = self
  

Есть предложения? Спасибо!

Макет

Ответ №1:

Вам гораздо больше повезет с автоматической компоновкой — он может обрабатывать все размеры фреймов и .contentSize для вас.

Вот краткий пример — он использует контроллер просмотра с видом прокрутки, добавленным в раскадровку, поэтому вам должно быть довольно легко интегрироваться с вашим кодом:

 class ScrollingImagesViewController: UIViewController {
    
    @IBOutlet var scrollView: UIScrollView!
    
    var drinksImagesArray: [String] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // however you're populating your array...
        drinksImagesArray = [
            "drink1",
            "drink2",
            "drink3",
            // etc...
        ]

        // create a horizontal stack view
        let stack = UIStackView()
        stack.axis = .horizontal
        stack.alignment = .fill
        stack.distribution = .fillEqually
        stack.spacing = 0

        // add the stack view to the scroll view
        stack.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stack)
        
        // use scroll view's contentLayoutGuide for content constraints
        let svCLG = scrollView.contentLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // stack view constrained Top / Bottom / Leading / Trailing of scroll view CONTENT guide
            stack.topAnchor.constraint(equalTo: svCLG.topAnchor),
            stack.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor),
            stack.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor),
            
            // stack view height == scroll view FRAME height
            stack.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
            
        ])

        // create image views and add them to the stack view
        drinksImagesArray.forEach { imgName in
            let v = UIImageView()
            v.backgroundColor = .lightGray
            v.contentMode = .scaleAspectFit
            // make sure we load a valid image
            if let img = UIImage(named: imgName) {
                v.image = img
            }
            stack.addArrangedSubview(v)
        }
        
        // stack distribution is set to .fillEqually, so we only need to set the
        // width constraint on the first image view
        
        // unwrap it
        if let firstImageView = stack.arrangedSubviews.first {
            firstImageView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor).isActive = true
        }
        
    }

}
  

Редактировать

После просмотра вашей раскадровки…

Авто-макету, похоже, не нравится, когда вы добавляете a UINavigationBar и a UIToolbar и a UIScrollView в качестве подвидов. В частности, похоже, что это сбивает с толку ограничения, связанные с рамкой прокрутки.

Исправление заключается в том, чтобы сначала добавить ограничения для вашего вида прокрутки:

  • Сверху донизу панели навигации
  • Управление снизу вверх страницы
  • Начало и завершение в безопасную область

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

введите описание изображения здесь

Затем в вашем классе view controller нам нужно создать ограничение высоты для представления стека, которое мы добавляем в представление прокрутки, и установить эту постоянную высоту в viewDidLayoutSubviews() .

Вот полный код:

 //
//  WasserhaushaltViewController.swift
//  deSynthTheOceans
//
//  Created by robinsonhus0 on 24.03.20.
//  Copyright © 2020 robinsonhus0. All rights reserved.
//

import UIKit
import AVFoundation
import Charts
import FSCalendar
import HealthKit


struct WasserSpeicher: Codable {
    let wassermenge: Double
    let speicherdatum: String
    let speicherStelle: Double
}

class WasserhaushaltViewController: UIViewController, UIScrollViewDelegate {
    @IBOutlet weak var diagrammView: UIView!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var pageControl: UIPageControl!
    
    let drinksImagesArray = ["tapWater", "water", "milk", "cola", "coffee", "tea", "juice", "beer"]

    var imageIndex = Int()
    
    struct Drinks {
        var name: String
        var tagesMengeFactor: Double
        var gesamtMengeFactor: Double
    }
    
    var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
    var pageNumber = CGFloat()
    
    @IBOutlet weak var todaysWaterConsumptionLabel: UILabel!
    @IBOutlet weak var waterGoalProgress: UIProgressView!
    @IBOutlet weak var waterGoalLabel: UILabel!
    @IBOutlet weak var wasserMengeStepper: UIStepper!
    @IBOutlet weak var motivationTextView: UITextView!
    @IBOutlet weak var wasserglasButton: UIBarButtonItem!
    @IBOutlet weak var kleineFlascheButton: UIBarButtonItem!
    @IBOutlet weak var grosseFlascheButton: UIBarButtonItem!
    @IBOutlet weak var overAllWaterConsumptionLabel: UILabel!
    
    // added
    let scrollingImagesStackView = UIStackView()
    var stackHeightConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        pageControl.numberOfPages = drinksImagesArray.count
        setupDrinkImages()
    }
    
    
    func setupDrinkImages() {
        // set stack view properties
        scrollingImagesStackView.axis = .horizontal
        scrollingImagesStackView.alignment = .fill
        scrollingImagesStackView.distribution = .fillEqually
        scrollingImagesStackView.spacing = 0
        
        // add the stack view to the scroll view
        scrollingImagesStackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(scrollingImagesStackView)
        
        // use scroll view's contentLayoutGuide for content constraints
        let svCLG = scrollView.contentLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // stack view constrained Top / Bottom / Leading / Trailing of scroll view CONTENT guide
            scrollingImagesStackView.topAnchor.constraint(equalTo: svCLG.topAnchor),
            scrollingImagesStackView.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor),
            scrollingImagesStackView.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor),
            scrollingImagesStackView.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor),
            
        ])
        
        // create the stack view height constraint - it will be updated in viewDidLayoutSubviews
        stackHeightConstraint = scrollingImagesStackView.heightAnchor.constraint(equalToConstant: 0)
        stackHeightConstraint.isActive = true
        
        // create image views and add them to the stack view
        drinksImagesArray.forEach { imgName in
            let v = UIImageView()
            v.backgroundColor = .orange
            v.contentMode = .scaleAspectFit
            // make sure we load a valid image
            if let img = UIImage(named: imgName) {
                v.image = img
            }
            scrollingImagesStackView.addArrangedSubview(v)
        }
        
        // stack distribution is set to .fillEqually, so we only need to set the
        // width constraint on the first image view
        
        // unwrap it
        if let firstImageView = scrollingImagesStackView.arrangedSubviews.first {
            firstImageView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor).isActive = true
        }

        scrollView.delegate = self
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        // since we have a UINavigationBar and a UIToolBar in the view hierarchy,
        //  we need to set this here
        //  Note: if the view size changes
        // stack view height == scroll view FRAME height
        stackHeightConstraint.constant = scrollView.frame.height
        
    }
    
//  func setupDrinkImages() {
//      for index in 0..<drinksImagesArray.count {
//          frame.origin.x = scrollView.frame.size.width * CGFloat(index)
//          frame.size = scrollView.frame.size
//
//          let imageView = UIImageView(frame: frame)
//          imageView.contentMode = .scaleAspectFit
//          imageView.image = UIImage(named: drinksImagesArray[index])
//          self.scrollView.addSubview(imageView)
//      }
//      scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(drinksImagesArray.count), height: scrollView.frame.size.height)
//      scrollView.delegate = self
//  }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
        pageControl.currentPage = Int(pageNumber)
    }
}
  

Ваша (измененная) раскадровка слишком велика, чтобы добавлять ее сюда … если у вас возникли какие-либо проблемы с упомянутыми выше изменениями, вот она:https://pastebin.com/2Q1uFUgL

Комментарии:

1. большое вам спасибо.! Это довольно здорово, но теперь изображения перекрывают Navigationcontroller и pagecontrol.

2. Это не имеет особого смысла … для того, чтобы изображения перекрывали что-то, выходящее за пределы scrollview , вам нужно было бы установить scrollView.clipsToBounds = false и установить высоту представления стека, превышающую высоту рамки просмотра прокрутки. Если вы разместите исходный код для своей раскадровки и код, который вы используете в pastebin.com Я посмотрю.

3. @JcbPrn — даже если бы мы попытались использовать teamView, я бы попросил то же самое… просто разместите исходный код своей раскадровки и код вашего класса на pastebin.com (или разместите весь свой проект на GitHub).

4. вот раскадровка: pastebin.com/5uwBcxPB и вот мой код Swift: pastebin.com/0Atmzn8B спасибо, что ответили так быстро

5. @JcbPrn — смотрите Редактирование внизу моего ответа.