Swift: не удается центрировать ImageView в ScrollView

#swift #uiscrollview #uiimageview

#swift #uiscrollview #uiimageview

Вопрос:

Я пытаюсь отобразить imageView в центре по осям X и Y на scrollView , которое хорошо вписывается в ширину устройства, когда я запускаю приложение. Я бы также хотел панорамировать и масштабировать imageView . Я перепробовал почти все решения, опубликованные на SO, а также следовал этому руководству, но почему-то это просто не решает мою проблему. Изображение сгенерировано из PDF.

Я делаю свой макет программно. Моя реализация до сих пор:

 let scrollView: UIScrollView = {
    let sv = UIScrollView()
    sv.translatesAutoresizingMaskIntoConstraints = false
    sv.backgroundColor = .lightGray
    return sv
}()

let imageView: UIImageView = {
    let iv = UIImageView()
    iv.translatesAutoresizingMaskIntoConstraints = false
    iv.contentMode = .scaleAspectFill
    return iv
}()

override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.delegate = self

    view.addSubview(scrollView)
    scrollView.addSubview(imageView)

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

        imageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10),
        imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10),
        imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10),
        imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -10),

        ])

    imageView.image = obtainThumbnail()
}

fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {
    let widthScale = size.width / imageView.bounds.width
    let heightScale = size.height / imageView.bounds.height
    let minScale = min(widthScale, heightScale)

    scrollView.minimumZoomScale = minScale
    scrollView.zoomScale = minScale
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    updateMinZoomScaleForSize(view.bounds.size)
}

func obtainThumbnail() -> UIImage? {
    guard let url = Bundle.main.url(forResource: "drawings", withExtension: "pdf") else {return nil}

    guard
        let data = try? Data(contentsOf: url),
        let page = PDFDocument(data: data)?.page(at: 0) else {
            return nil
    }

    let pageSize = page.bounds(for: .mediaBox)
    return page.thumbnail(of: CGSize(width: pageSize.width, height: pageSize.height), for: .mediaBox)
}

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imageView
}
  

Это мои желаемые результаты при запуске приложения.
введите описание изображения здесь

И это результаты, когда я запускаю приложение сейчас, перегруженное. введите описание изображения здесь

Кто-нибудь посоветует, чего мне не хватает?

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

1. Попробуйте изменить contentMode = .scaleAspectFill на scaleAspectFit

2. @Magnas попробовал, тот же результат, что и выше

3. При вычислении minScale в updateMinZoomScaleForSize кадрах обоих scrollView и imageView равны нулю.

4. @Magnas если это так, где я должен вычислять?

Ответ №1:

Я думаю, что представления загружаются лениво в UIViewController s, поэтому выполнение вычисления позже в жизненном цикле пользовательского интерфейса должно решить проблему. Удаляем вызов в viewWillLayoutSubviews и вместо этого используем:

 override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    updateMinZoomScaleForSize(view.bounds.size)
}
  

кажется, работает.

Ответ №2:

Чтобы это работало хорошо, нужно исправить два места:

   override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.delegate = self

    view.addSubview(scrollView)
    scrollView.addSubview(imageView)

    NSLayoutConstraint.activate([

        scrollView.topAnchor.constraint(equalTo: view.topAnchor),
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),

       //First place.
       // scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
       // scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),
        scrollView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),



        imageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10),
        imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10),
        imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10),
        imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -10),


        ])

    imageView.backgroundColor = UIColor.red
    imageView.image = UIImage.init(named: "temp2.png")
     //second place.
    imageView.sizeToFit()
  

Надеюсь, это хорошо решило вашу проблему.

Если вам нужен вид посередине, попробуйте следующий метод:

 let scrollView: UIScrollView = {
    let sv = UIScrollView()
    sv.translatesAutoresizingMaskIntoConstraints = false
    sv.backgroundColor = .lightGray
  sv.contentMode = .center

 return sv
}()

 let imageView: UIImageView = {
    let iv = UIImageView()
    iv.translatesAutoresizingMaskIntoConstraints = true
    iv.contentMode = .scaleAspectFill
    return iv
}()

override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.delegate = self

    view.addSubview(scrollView)
    scrollView.addSubview(imageView)



    NSLayoutConstraint.activate([


     scrollView.centerYAnchor.constraint(equalTo: view.centerYAnchor ),

    scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor ),


        scrollView.contentLayoutGuide.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
   scrollView.contentLayoutGuide.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),


        ])

    imageView.backgroundColor = UIColor.red
    imageView.image = UIImage.init(named: "temp2.png")
    imageView.sizeToFit()
    updateMinZoomScaleForSize(view.bounds.size)


}


var constantHeight : CGFloat{

    return  min(view.bounds.height, imageView.bounds.height *  scrollView.zoomScale)
}
var constantWidth : CGFloat{
    return min( view.bounds.width, imageView.bounds.width *  scrollView.zoomScale)
}

    lazy var constraintHeight: NSLayoutConstraint = {

     return      scrollView.contentLayoutGuide.heightAnchor.constraint(equalToConstant: constantHeight)

}()

    lazy  var constraintWeight: NSLayoutConstraint = {
    return
        scrollView.contentLayoutGuide.widthAnchor.constraint(equalToConstant: constantWidth)

}()



fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {

    let widthScale = size.width / imageView.bounds.width
    let heightScale = size.height / imageView.bounds.height
    let minScale = min(widthScale, heightScale)

    imageView.frame = CGRect.init(x: 0, y: 0, width: minScale * imageView.bounds.width, height: minScale*imageView.bounds.height)


    scrollView.maximumZoomScale = 10.0
    scrollView.zoomScale = 1.0



    constraintHeight.isActive = true
  constraintWeight.isActive = true


}




func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat){

    constraintHeight.constant = constantHeight
    constraintWeight.constant  = constantWidth
    scrollView.clipsToBounds = true

}


func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
   scrollView.clipsToBounds = false
}


func viewForZooming(in scrollView: UIScrollView) -> UIView? {
    return imageView
}
  

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

1. ваше решение вроде бы сработало, но изображение все еще не подходит по ширине. Более того, hard coding 200 от topAnchor не является масштабируемым решением. Мне понадобится эта работа для всех устройств.