I have tableView that uses a NSFetchedResultsController to populate data. When clicking on a cell, it takes you to a detailViewController of that object. And the following two properties are pushed forward with prepare(for:).
var coreDataStack: CoreDataStack!
var selectedGlaze: Glaze?
Inside the detailView, I have 2 cells. The first is cell that contains a scrollView with an array of images:
import UIKit
protocol SwipedRecipeImageViewDelegate: class {
func recipeImageViewSwiped(_ cell: RecipePhotoTableViewCell, selectInt: Int)
}
class RecipePhotoTableViewCell: UITableViewCell, UIScrollViewDelegate {
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var pageControl: UIPageControl!
// -
var imagesArray: [Data] = []
var selectedImageData: Int = 0
// -
weak var delegate: SwipedRecipeImageViewDelegate?
// -
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = .clear
self.selectionStyle = .none
scrollView.delegate = self
scrollView.isUserInteractionEnabled = false // Allows didSelectAtRow:
contentView.addGestureRecognizer(scrollView.panGestureRecognizer) // Allows Scrolling
}
override func layoutSubviews() {
super.layoutSubviews()
setImages()
setOffsetX(pageNumber: selectedImageData)
}
func configureCell(section: Int, row: Int, images: [RecipeImage], arrayInt: Int, delegate: SwipedRecipeImageViewDelegate) {
self.delegate = delegate
selectedImageData = arrayInt
for image in images {
guard let imageData = image.recipeImageData else { return }
imagesArray.append(imageData)
}
}
@IBAction func pageChanged(_ sender: UIPageControl) {
setOffsetX(pageNumber: sender.currentPage)
}
func setOffsetX(pageNumber: Int) {
pageControl.currentPage = pageNumber
let offsetX = contentView.bounds.width * CGFloat(pageNumber)
DispatchQueue.main.async {
UIView.animate(withDuration: 0.2, delay: 0, options: UIView.AnimationOptions.curveEaseOut, animations: {
self.scrollView.contentOffset.x = offsetX
}, completion: nil)
}
}
func setImages() {
// Set Page Count:
pageControl.numberOfPages = imagesArray.count
// Set Frame For ImageViews + Scroll View:
for index in 0..<imagesArray.count {
let imageView = UIImageView()
imageView.frame.size = contentView.bounds.size
imageView.frame.origin.x = contentView.bounds.width * CGFloat(index)
imageView.image = UIImage(data: imagesArray[index])
imageView.contentMode = .scaleAspectFill
imageView.layer.masksToBounds = true // Limits Frame Size
scrollView.addSubview(imageView)
}
// Set ScrollView Size:
scrollView.contentSize = CGSize(width: (contentView.bounds.width * CGFloat(imagesArray.count)), height: contentView.bounds.height)
scrollView.delegate = self
}
// Set Page Number:
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
self.pageControl.currentPage = Int(pageNumber)
delegate?.recipeImageViewSwiped(self, selectInt: pageControl.currentPage)
}
The second cell contains a stackView with some labels to display data that the image shows. It accepts a lot of parameters and then sets the textColor and changes some labels. Nothing too exciting so I didn't include the code.
DetailViewController:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cellForRowAt: ", indexPath)
switch indexPath.section {
case sectionImage: // Section 0:
guard
let images = selectedGlaze?.glazeImage,
let glazeImageSelected = selectedGlaze?.glazeImageSelected // This is a Double
else { return returnDefaultCell() }
let imageArray = images.allObjects as! [RecipeImage] // Takes NSSet of relational data and changes it into an Array to be passed into the image cell.
let imageSelected = Int(glazeImageSelected) // Double Converted to Int
switch indexPath.row {
case 0:
let cell = returnRecipeImageCell()
return configureRecipeImageCell(cell: cell, for: indexPath, imagesArray: imageArray, imageSelected: imageSelected)
case 1:
let cell = returnAtmosphereCell()
return configureAtmosphereCell(cell: cell, for: indexPath, imagesArray: imageArray, imageSelected: imageSelected)
default: return returnDefaultCell()
}
}
}
SwipedRecipeImageViewDelegate:
func recipeImageViewSwiped(_ cell: RecipePhotoTableViewCell, selectInt: Int) {
selectedGlaze?.glazeImageSelected = Double(selectInt)
coreDataStack.saveContext()
DispatchQueue.main.async { //
self.tableView.beginUpdates() //
let row1: IndexPath = [0,1] //
self.tableView.reloadRows(at: [row1], with: .automatic) //
self.tableView.endUpdates() //
}
}
The Issue:
The issue I'm having is reloading the second cell to be updated with the correct information after the recipeImageViewSwiped() is called. Seen here: https://imgur.com/a/fIYfehf
This happens when the code inside the DispatchQueue.main.async block is active. When the block is comment out, this happens: https://imgur.com/a/fYUVZKH - Which is what I'd expect. (Other than the cell at [0,1] isn't updated).
Specifically, when the tableView reloads row [0,1], cellForRowAt() only gets called on that row, [0,1]. But I'm not sure why the cell at [0,0], with the image, flicks back to the original image shown in the scrollView.
Goal: My goal is to have the cell with the scrollView not flicker after being swiped on. But also to save the context, so that the object can save which image in the array is selected. And then to update/reload the second cell with the new information the image that's selected, so it can update it's labels correctly.
EDIT:
Removing the following in layoutSubviews() has this affect: https://imgur.com/a/vwrZfus - Which looks like it's mostly working. But still has a strange animation.
override func layoutSubviews() {
super.layoutSubviews()
setImages()
// setOffsetX(pageNumber: selectedImageData)
}
EDIT 2: This looks like its entirely an issue with setting up the cell's view. Along with layout Subviews.
EDIT 3:
I added a Bool: hasSetLayout and a switch inside of layoutSubviews() and it appears to be working as I want. - However if any one still has any information to help me understand this issue, I'd appreciate it.
var hasSetLayout = false
override func layoutSubviews() {
super.layoutSubviews()
switch hasSetLayout {
case false: setImages(selectedPhoto: selectedImageData)
default: break
}
}