I’ve a set view. Once I contact the cell, the animation from the gathering view cell class CarouselCell
enjoying. Once I press on a cell, the method of downloading the file begins within the technique didSelectItemAt
. To replace the method I take advantage of the code from right here reloadItem(indexPath: IndexPath)
The issue is that if whereas downloading a file I attempt to contact a cell, the animation will play as soon as, like a soar or hitch, after which it received’t work in any respect when touched till the file is downloaded. Additionally, throughout downloading, when the cell is clicked once more, the strategy didSelectItemAt
is not going to be referred to as till the file is downloaded. The truth that the didSelectItemAt
technique just isn’t referred to as throughout file downloading is sweet. This prevents the identical file from being downloaded a number of occasions. However the issue is that I don’t perceive why this occurs as a result of I haven’t written such code wherever. How you can repair the animation and why didSelectItemAt
not referred to as throughout the file is downloading?
code:
I eliminated all of the obtain associated code to save lots of house in query and changed it with a timer to simulate the file obtain progress
assortment:
class CarouselController: UIViewController, UICollectionViewDelegate {
var progress = 0.0
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Part, Merchandise>?
let sections = Bundle.primary.decode([Section].self, from: "carouselData.json")
var objects = [DownloadItem]()
override func viewDidLoad() {
tremendous.viewDidLoad()
setupCollectionView()
setupScrollView()
setupDownloadItems()
}
func setupScrollView() {
collectionView.scrollToItem(at: IndexPath(merchandise: 0, part: 0), at: .centeredHorizontally, animated: false)
}
func setupDownloadItems() {
let rely = dataSource!.snapshot().numberOfItems
for index in 0...rely { objects.append(DownloadItem(merchandise: index)) }
}
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Part, Merchandise>(collectionView: collectionView) { collectionView, indexPath, merchandise in
change self.sections[indexPath.section].identifier {
case "carouselCell":
let cell = self.configure(CarouselCell.self, with: merchandise, for: indexPath)
if indexPath.row < self.objects.rely {
let merchandise = self.objects[indexPath.row]
cell.title.textual content = "(String(format: "%.f%%", merchandise.progress * 100))"
}
return cell
default: return self.configure(CarouselCell.self, with: merchandise, for: indexPath)
}
}
}
func configure<T: SelfConfiguringCell>(_ cellType: T.Kind, with merchandise: Merchandise, for indexPath: IndexPath) -> T {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else { fatalError("(cellType)") }
cell.configure(with: merchandise)
return cell
}
func reloadData() {
var snapshot = NSDiffableDataSourceSnapshot<Part, Merchandise>()
snapshot.appendSections(sections)
for part in sections { snapshot.appendItems(part.merchandise, toSection: part) }
dataSource?.apply(snapshot)
}
func setupCollectionView() {
collectionView = UICollectionView(body: view.bounds, collectionViewLayout: createCompositionalLayout())
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.isScrollEnabled = false
collectionView.delegate = self
collectionView.contentInsetAdjustmentBehavior = .by no means
view.addSubview(collectionView)
collectionView.register(CarouselCell.self, forCellWithReuseIdentifier: CarouselCell.reuseIdentifier)
createDataSource()
reloadData()
}
func createCompositionalLayout() -> UICollectionViewLayout {
UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(1))
let merchandise = NSCollectionLayoutItem(layoutSize: itemSize)
let groupWidth = (layoutEnvironment.container.contentSize.width * 1.05)/3
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth),
heightDimension: .absolute(groupWidth))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let part = NSCollectionLayoutSection(group: group)
part.contentInsets = NSDirectionalEdgeInsets(
prime: (layoutEnvironment.container.contentSize.peak/2) - (groupWidth/2),
main: 0,
backside: 0,
trailing: 0)
part.interGroupSpacing = 64
part.orthogonalScrollingBehavior = .groupPagingCentered
part.contentInsetsReference = .none
part.visibleItemsInvalidationHandler = { (objects, offset, atmosphere) in
objects.forEach { merchandise in
let distanceFromCenter = abs((merchandise.body.midX - offset.x) - atmosphere.container.contentSize.width / 2.0)
let minScale: CGFloat = 0.7
let maxScale: CGFloat = 1.1
let scale = max(maxScale - (distanceFromCenter / atmosphere.container.contentSize.width), minScale)
merchandise.rework = CGAffineTransform(scaleX: scale, y: scale)
}
}
return part
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("-")
_ = Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true) { timer in
guard self.progress <= 1.0 else {
timer.invalidate()
self.progress = 0.0
return
}
self.progress += 0.01
self.objects[indexPath.row].progress = Float(self.progress)
self.reloadItem(indexPath: .init(row: indexPath.row, part: 0))
}
}
func reloadItem(indexPath: IndexPath) {
guard let needReloadItem = dataSource!.itemIdentifier(for: indexPath) else { return }
guard var snapshot = dataSource?.snapshot() else { return }
snapshot.reloadItems([needReloadItem])
dataSource?.apply(snapshot, animatingDifferences: false)
}
}
cell
class CarouselCell: UICollectionViewCell, SelfConfiguringCell {
static let reuseIdentifier: String = "carouselCell"
var topSpaceView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var imageView: UIImageView = {
let picture = UIImageView()
picture.contentMode = .scaleAspectFill
picture.translatesAutoresizingMaskIntoConstraints = false
return picture
}()
var titleImageView: UIImageView = {
let picture = UIImageView()
picture.contentMode = .scaleAspectFill
picture.translatesAutoresizingMaskIntoConstraints = false
return picture
}()
var textView: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.layer.borderColor = UIColor.crimson.cgColor
view.layer.borderWidth = 3
view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var title: UILabel = {
let label = UILabel()
label.textColor = .white
label.textAlignment = .middle
label.sizeToFit()
label.font = UIFont(title: "Marker Felt", measurement: 24)
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var progressView: UIProgressView = {
let view = UIProgressView(progressViewStyle: .bar)
view.middle = view.middle
view.setProgress(0.0, animated: true)
view.trackTintColor = .clear
view.tintColor = .white.withAlphaComponent(0.15)
view.clipsToBounds = true
view.layer.borderColor = UIColor.crimson.cgColor
view.layer.borderWidth = 3
view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var bottomSpaceView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var stackView: UIView = {
let stack = UIView()
stack.layer.shadowColor = UIColor.black.cgColor
stack.layer.shadowOffset = CGSize.zero
stack.layer.shadowOpacity = 0.6
stack.layer.shadowRadius = 3
stack.translatesAutoresizingMaskIntoConstraints = false
return stack
}()
override var isHighlighted: Bool {
didSet { if oldValue == false && isHighlighted { spotlight() }
else if oldValue == true && !isHighlighted { unHighlight() }
}
}
var imageTask: AnyCancellable?
var titleImageTask: AnyCancellable?
override func prepareForReuse() {
tremendous.prepareForReuse()
imageView.picture = nil
titleImageView.picture = nil
imageTask?.cancel()
}
override init(body: CGRect) {
tremendous.init(body: body)
setupViews()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("error")
}
func setupViews() {
stackView.addSubview(topSpaceView)
stackView.addSubview(imageView)
stackView.addSubview(titleImageView)
stackView.addSubview(textView)
stackView.addSubview(bottomSpaceView)
contentView.addSubview(stackView)
textView.addSubview(progressView)
textView.addSubview(title)
stackView.sendSubview(toBack: textView)
textView.bringSubview(toFront: title)
}
func setupConstraints() {
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: contentView.topAnchor, fixed: 0).isActive = true
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, fixed: 0).isActive = true
topSpaceView.topAnchor.constraint(equalTo: stackView.topAnchor, fixed: 0).isActive = true
topSpaceView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.07).isActive = true
topSpaceView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
topSpaceView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: topSpaceView.bottomAnchor, fixed: 0).isActive = true
imageView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.7).isActive = true
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, fixed: 0).isActive = true
imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, fixed: 0).isActive = true
titleImageView.topAnchor.constraint(equalTo: topSpaceView.bottomAnchor, fixed: 0).isActive = true
titleImageView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.7).isActive = true
titleImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, fixed: 0).isActive = true
titleImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, fixed: 0).isActive = true
textView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.16).isActive = true
textView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, fixed: 24).isActive = true
textView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, fixed: -24).isActive = true
textView.topAnchor.constraint(equalTo: imageView.bottomAnchor, fixed: -6).isActive = true
textView.layer.cornerRadius = contentView.body.measurement.peak*0.16/2
title.leadingAnchor.constraint(equalTo: textView.leadingAnchor, fixed: 0).isActive = true
title.trailingAnchor.constraint(equalTo: textView.trailingAnchor, fixed: 0).isActive = true
title.topAnchor.constraint(equalTo: textView.topAnchor, fixed: 0).isActive = true
title.bottomAnchor.constraint(equalTo: textView.bottomAnchor, fixed: 0).isActive = true
progressView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.16).isActive = true
progressView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, fixed: 24).isActive = true
progressView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, fixed: -24).isActive = true
progressView.topAnchor.constraint(equalTo: imageView.bottomAnchor, fixed: -6).isActive = true
progressView.layer.cornerRadius = contentView.body.measurement.peak*0.16/2
bottomSpaceView.topAnchor.constraint(equalTo: textView.bottomAnchor, fixed: 0).isActive = true
bottomSpaceView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.07).isActive = true
bottomSpaceView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
bottomSpaceView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
}
personal func animateScale(to scale: CGFloat, period: TimeInterval) {
UIView.animate( withDuration: period, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, choices: [.beginFromCurrentState], animations: {
self.stackView.rework = .init(scaleX: scale, y: scale)
self.textView.rework = .init(scaleX: scale, y: scale)
}, completion: nil)
}
func spotlight() {
animateScale(to: 0.9, period: 0.4)
}
func unHighlight() {
animateScale(to: 1, period: 0.38)
}
func configure(with merchandise: Merchandise) {
title.textual content = merchandise.title
textView.backgroundColor = .inexperienced
textView.layer.borderColor = UIColor.inexperienced.cgColor
progressView.layer.borderColor = UIColor.inexperienced.cgColor
titleImageTask = Future<UIImage?, By no means>() { promise in
UIImage(named: merchandise.titleImage)?.prepareForDisplay(completionHandler: { loadedImage in
promise(End result.success(loadedImage))
})
}
.obtain(on: DispatchQueue.primary)
.sink { picture in
self.titleImageView.picture = picture
}
imageTask = Future<UIImage?, By no means>() { promise in
UIImage(named: merchandise.picture)?.prepareForDisplay(completionHandler: { loadedImage in
promise(End result.success(loadedImage))
})
}
.obtain(on: DispatchQueue.primary)
.sink { picture in
self.imageView.picture = picture
}
}
}
Additionally tried utilizing touchesBegan
/touchesEnded
as a substitute of isHighlighted however the outcome was the identical:
override func touchesBegan(_ touches: Set<UITouch>, with occasion: UIEvent?) {
tremendous.touchesBegan(touches, with: occasion)
spotlight()
}
override func touchesEnded(_ touches: Set<UITouch>, with occasion: UIEvent?) {
tremendous.touchesEnded(touches, with: occasion)
unHighlight()
}
override func touchesCancelled(_ touches: Set<UITouch>, with occasion: UIEvent?) {
tremendous.touchesCancelled(touches, with: occasion)
unHighlight()
}
extra:
extension Bundle {
func decode<T: Decodable>(_ kind: T.Kind, from file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Did not find (file) in bundle.")
}
guard let knowledge = strive? Information(contentsOf: url) else {
fatalError("Did not load (file) from bundle.")
}
let decoder = JSONDecoder()
guard let loaded = strive? decoder.decode(T.self, from: knowledge) else {
fatalError("Did not decode (file) from bundle.")
}
return loaded
}
}
protocol SelfConfiguringCell {
static var reuseIdentifier: String { get }
func configure(with merchandise: Merchandise)
}
public struct Part: Decodable, Hashable {
let index: Int
let identifier: String
let title: String
let subtitle: String
let merchandise: [Item]
}
public struct Merchandise: Decodable, Hashable {
let index: Int
let title: String
let picture: String
let titleImage: String
let backgroundColor: String
let borderColor: String
}
json
[
{
"index": 1,
"identifier": "carouselCell",
"title": "",
"subtitle": "",
"item": [
{
"index": 1,
"title": "0",
"image": "cover1",
"titleImage": "title1",
"backgroundColor": "5896CC",
"borderColor": "3274AF",
},
{
"index": 2,
"title": "0",
"image": "cover2",
"titleImage": "title2",
"backgroundColor": "895138",
"borderColor": "703A22",
},
{
"index": 3,
"title": "0",
"image": "cover3",
"titleImage": "title3",
"backgroundColor": "804136",
"borderColor": "62322A",
},
{
"index": 4,
"title": "0",
"image": "cover4",
"titleImage": "title4",
"backgroundColor": "3E78A3",
"borderColor": "2A597D",
},
]
},
]