Как использовать UICollectionViewCompositionalLayout для правильной визуализации удаленного изображения в UIKit

Я хочу отобразить NSCollectionLayoutItem (содержит изображение с удаленного устройства, а его соотношение сторон неизвестно), занять экран устройства по горизонтали и при этом получить правильное соотношение сторон по вертикали.

Поэтому я использую точки NSCollectionLayoutDimension.estimated(50) для высоты. но после загрузки изображения высота по-прежнему равна 50, чего недостаточно для отображения изображения.

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

ссылка на изображение находится здесь: [image src] (https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png)

содержание изображения:

и результат работы будет таким

код конфигурации UICollectionView:

import UIKit

class ViewController: UIViewController {
    enum Section {
        case main
    }
    
    @IBOutlet weak var collectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource<Section, Int>! = nil
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.configDataSource()
        self.configLayout()
    }

    
    func configDataSource() {
        let imageCellReg = UICollectionView.CellRegistration<ImageCell,Int>(cellNib: UINib(nibName: "ImageCell", bundle: nil)) { cell, indexPath, itemIdentifier in
            
        }
        self.dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: self.collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
            return self.collectionView.dequeueConfiguredReusableCell(using: imageCellReg, for: indexPath, item: itemIdentifier)
        })
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
        snapshot.appendSections([.main])
        snapshot.appendItems(Array(0..<2))
        dataSource.apply(snapshot, animatingDifferences: false)

    }
    
    func configLayout(){
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                             heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension:.estimated(50))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize:groupSize, subitems: [item])
        let spacing = CGFloat(10)
        group.interItemSpacing = .fixed(spacing)

        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = spacing
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)

        let layout = UICollectionViewCompositionalLayout(section: section)
        self.collectionView.collectionViewLayout = layout
    }

}

И код ячейки:

import UIKit
import SDWebImage

class ImageCell: UICollectionViewCell {
    @IBOutlet weak var imageContentView: UIImageView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        imageContentView.sd_setImage(with: URL(string: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"))
    }

}

Это отличается от вашего предыдущего вопроса? stackoverflow.com/questions/78370684/…

DonMag 24.04.2024 15:02

@DonMag, это более точная проблема из предыдущего вопроса

ximmyxiao 25.04.2024 02:48

Лучше не публиковать новый вопрос — лучше продолжить обсуждение с людьми, которые уже ответили. Я бы посоветовал вам зайти в свою любимую поисковую систему и поискать SDWebImage set uicollectionviewcell height after download... уже существует множество обсуждений и примеров.

DonMag 25.04.2024 14:50

@DonMag, это не имеет ничего общего с SDWebImage. На самом деле проблема в том, что вы не можете установить правильную высоту для ячейки, если сначала не знаете точное соотношение сторон. И если вы сбросите ячейку после загрузки с соотношением сторон просто получите. В большинстве ситуаций это означает повторную перезагрузку изображения. и вызвать бесконечный цикл (возможно, есть какие-то способы избежать этого цикла), но я думаю, что для этого сценария должна быть хорошая практика.

ximmyxiao 26.04.2024 08:05

@DonMag, может быть, ты сможешь написать демо-версию для отображения изображения значка Google с удаленного компьютера с помощью uicollectionviewcompositionallayout, и ты поймешь, что я путаю ~

ximmyxiao 26.04.2024 08:16

@ximmyxiao, я получаю вот такой результат ibb.co/wpNqrQs

Shivbaba's son 26.04.2024 14:31

используется тот же код? Это нормально ?

Shivbaba's son 26.04.2024 14:41

@Shivbaba'sson, спасибо, код DonMan для меня очень полезен ~

ximmyxiao 28.04.2024 04:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Очень быстрый пример...

Создайте свою ячейку следующим образом:

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

Класс ячейки очень прост:

class ImageCell: UICollectionViewCell {

    @IBOutlet var imageContentView: UIImageView!
    @IBOutlet var imgHeight: NSLayoutConstraint!
    
}

Используя SDWebImage, мы реализуем блок завершения, в котором рассчитаем высоту соотношения сторон для загруженного изображения, которую мы будем использовать для установки imgHeight.constant. Затем мы вызываем collectionView.collectionViewLayout.invalidateLayout(), чтобы сообщить контроллеру изменить расположение ячеек:

cell.imageContentView.sd_setImage (
    with: URL(string: urlStr),
    placeholderImage: UIImage(named: "somePlaceholder"),
    options: SDWebImageOptions(rawValue: 0),
    completed: { [weak self] image, error, cacheType, imageURL in
        guard let self = self,
              let img = image
        else { return }
        
        // calculate proportional height
        let iw = cell.imageContentView.frame.width
        let ih = (img.size.height / img.size.width) * iw
        
        // update the height contraint in the cell
        cell.imgHeight.constant = ih
        
        // tell the collection view to re-layout the cells
        self.collectionView.collectionViewLayout.invalidateLayout()
    }
)

        

Вот полный пример проекта: https://github.com/DonMag/SDWebCV

Спасибо, похоже, validateLayout не перезагрузит ячейку, поэтому бесконечного цикла не будет, я использую для вызова reloadData/Item в блоке завершения. очень полезно это узнать~!

ximmyxiao 28.04.2024 04:25

Я вижу ограничение по высоте для ячейки, добавленной в проект. Должно ли это ограничение существовать? или функция самостоятельного определения размера может делать то же самое без ограничения высоты?

ximmyxiao 28.04.2024 04:38

@ximmyxiao - вам нужно что-то сделать, чтобы сообщить представлению коллекции, какой высоты сделать ячейку. Это один из подходов. Вы не предоставили никаких подробностей, связанных с чем-либо еще, что вы, возможно, захотите сделать со своим пользовательским интерфейсом... вы можете использовать собственный макет вместо CompositionalLayout, но я понятия не имею, какова ваша конечная цель.

DonMag 28.04.2024 14:42

Фактически, я реорганизую страницу с флаттера на собственный код iOS и обнаружил, что List+Image будет хорошо работать для изображений любого размера без необходимости сброса высоты. Итак, я хочу знать, можно ли сделать то же самое в собственном коде iOS. потому что я думал, что механизм самостоятельного определения размера в iOS может работать так же, как флаттер, и мне не нужно устанавливать высоту вручную. но похоже я ошибаюсь @DonMag

ximmyxiao 29.04.2024 04:05

@ximmyxiao — я ничего не знаю о Flutter или его компонентах пользовательского интерфейса. В iOS для UIImageView можно установить .scaleToFill, .scaleAspectFit, .scaleAspectFill (и некоторые менее распространенные режимы), чтобы контролировать размер изображения внутри рамки просмотра, но у него нет возможности «изменить рамку просмотра в соответствии с изображением». Вы могли бы создать подкласс UIImageView, чтобы получить эту функциональность, но... есть ли причина, по которой вы не хотите использовать метод ограничения, который я вам дал?

DonMag 29.04.2024 15:04

Спасибо @DonMag, потому что в большинстве случаев вам не нужно устанавливать ограничение высоты для ячейки, механизм самостоятельного определения размера UIKit установит правильную высоту для ячейки. (например, ячейка имеет только метку). Я думаю, что такой макет «ячейка без ограничения по высоте» более прост в использовании, чем макет «ячейка должна установить ограничение по высоте». Но, как вы говорите: само изображение не имеет возможности «изменить рамку просмотра в соответствии с изображением». Итак, кажется, что если я хочу создать ячейку без ограничения по высоте, мне нужно создать подкласс UIImageView и перезаписать его встроенныйContentSize

ximmyxiao 30.04.2024 07:12

@ximmyxiao - да, если вы хотите, чтобы компонент пользовательского интерфейса имел свойства/поведения, отличные от его по умолчанию, вам необходимо создать его подкласс. В опубликованном вами коде вы уже создаете подклассы UIViewController и UICollectionViewCell... поэтому создание подклассов UIImageView будет просто еще одним шагом для получения желаемых результатов.

DonMag 30.04.2024 14:17

Другие вопросы по теме

Расширьте класс Swift, который уже соответствует некоторому протоколу
Почему Swift выдает «Невозможно использовать мутирующий член для неизменяемого значения» в некоторых вызовах цепочки, но не в других?
Как установить делегата для частного API в Swift или Objective-C?
Приложение Flutter не может обнаружить определенные периферийные устройства с помощью библиотеки BluetoothCore IOS/Swift
Является ли предупреждение Swift 5.10 о «вероятно неправильном» фактически неправильным для необязательных CFType?
Неустранимая ошибка: не удалось декодировать данные .json из пакета приложения
Объединить один и тот же элемент основных данных из нескольких представлений таблиц в UIKit
Метод доступа без индекса для Swift Dictionary
Восстановление состояния навигации с помощью NavigationStack нарушает привязку
Измените атрибут метаданных файла kMDItemDateAdded («Дата добавления») с помощью кода Swift