UIScrollView position in reusable cell affecting UIScrollView position in other cell - ios

I'm building an iOS app with Swift where a user can scroll through a feed of images up and down (like instagram) and can also scroll left or right on the cell to see more images. Refer to this album(not able to upload images yet) and see F1 for how the UI is laid out.
My problem is when I scroll over to image 1C, the 3rd cell is also scrolling over to 3C. So when I scroll down to the third cell, it's already at 3C. See F2.
Additionally, if I scroll on the 3rd cell to 3B, it also repositions the first cell to 1B. See F3.
I'd like some help in understanding what's going on...
I've created a custom class for the cell with a function to load the images.
(Where I also initialize the imageView frames, scrollView frame and content size, and other properties which I'm not including in the code below.)
class CustomCell: UITableViewCell {
var imageView1: UIImageView = UIImageView()
var imageView2: UIImageView = UIImageView()
var imageView3: UIImageView = UIImageView()
func loadImages(#image1: String, image2: String, image3: String){
imageView1.image = UIImage(named: image1)
imageView2.image = UIImage(named: image2)
imageView3.image = UIImage(named: image3)
}}
and in my View Controller - cellForRowAtIndexPath
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:CustomCell = self.tableView.dequeueReusableCellWithIdentifier("CustomCell") as CustomCell
var (firstImage, secondImage, thirdImage) = images[indexPath.row]
cell.loadImages(image1: firstImage, image2: secondImage, image3: thirdImage)
return cell
}
FYI: This appears to only be happening when the view is the root view controller. It works fine when the view has been presented. ???
Thanks!

The UITableView dequeues reusable cells. That means that your horizontal scrollview in your cell will have the same content offset as the cell he dequeued. So if you scroll to let's say contentOffset.x = 100.0 in cell 1 and than scroll down in your tableview, it might happen that one of the following cells will have its scrollview subview with the same contentOffset.
Just set contentOffset.y = 0 in your cellForRowAtIndexPath.
Or better: Save the cell's selected imageIndex and set an appropriate contentOffset.

Reuseable means exactly that: to speed things up the cells gets reused. But as you are changing view properties these are present when the cell is populated with new values. Set the values back on population in cellF orRiwAtIndexPath or – cleaner IMHO – in cellWillDisplay.

Related

Table cell only updates when I select it

I'm making an iOS app which uses a table, and each cell contains two labels and a custom view I made called ColourCircle. ColourCircle has a property called fill: UIColor and I've overridden the draw(_ rect: CGRect) function to draw a circle of that colour.
The cells' data are loaded from an array: data: [(String, Double)], using the view controller as both the UITableViewDelegate and UITableViewDataSource.
In viewWillAppear I've written:
guard let fetchedData = getArtistData() else {
performSegue(withIdentifier: "data-error", sender: nil)
return
}
chart.load(data: fetchedData)
data = fetchedData
table.reloadData()
table.setNeedsLayout()
table.isHidden = false
chart.isHidden = false
activityIndicator.stopAnimating()
And, in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) I've written:
let cell = table.dequeueReusableCell(withIdentifier: "artist cell") as! ArtistCell
let name = data[indexPath.row].0
if let proportion = chart.getPercentage(of: name) {
let percentage = proportion * 100
cell.name.text = name
cell.percentage.text = "\(round(percentage * 10) / 10)%" // rounded to 2 d.p.
cell.colour.fill = chart.colour(for: name)!
}
return cell
When I run my app, the visible cells (i.e. which are on the screen) work perfectly, and the ColourCircle views display the correct colour. When I scroll down, bringing new cells into view, the new cells don't have the correct colour, but instead use the colours from the previously loaded cells, in order.
For example, say the first two cells in the table are red and blue, then the first two which load when I scroll down will also be red and blue, in that order. The same thing happens when I scroll up, re-loading the previous cells.
When I select these cells, however, they change to the correct colour. Then, when unloaded and loaded again, they will stay the correct colour unless I've fixed one of the cells further up (by selecting them), in which case they will change to the colours of the ones I fixed.
It seems like it's something to do with reusing the cells, although I can't quite work out how to fix it.
EDIT: Here's the code for my ArtistCell class:
class ArtistCell: UITableViewCell {
#IBOutlet weak var colour: ColourCircle!
#IBOutlet weak var name: UILabel!
#IBOutlet weak var percentage: UILabel!
}
according this: let cell = table.dequeueReusableCell(withIdentifier: "artist cell") as! ArtistCell - all cells are reusable. Use prepareForReuse() method, to prepare cell to reuse and clean up they content.

how to hide the image/label in StoryBoard to show up when we run the app?

I am making a table view app which retrieves the data from the Firebase. when making the user interface in the storyboard, I am using dummy image and label to visualize my app.
but when i run the app which consists of dynamic table view, those dummy images and label also shows up before immediately replaced by the actual data that i download from the Firebase storage.
can I set those images and labels to not show up when i run the app but still available in the storyboard?
thanks in advance :)
If they're just dummies, you can get rid of them when your view loads, before it appears onscreen:
override func viewDidLoad() -> Void{
super.viewDidLoad()
dummy.removeFromSuperview()
}
Whenever you want to hide/show an UIView:
myView.isHidden = true // hide
myView.isHidden = false // show
I assume what you need is to hide the views in viewWillAppear and then show them when necessary.
In your custom cell class, define something to hide the unwanted views:
func hideDummyViews() {
// do some stuff to hide what you don't want, e.g.
myEnclosingStackView.isHidden = true
}
In your table view controller, in the cellForRowAt indexPath func:
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell ..
if yourDataSource[indexPath.row].isCompletelyLoaded {
// do your fancy dynamic cell layout
} else {
// show the basic version (minus any dummy views)
cell.hideDummyViews()
}
return cell
You can choose your preferred method for hiding the items (isHidden for each view, removing, adjusting constraints). I prefer to embed any disappearing views in a stack view and then use isHidden = true on the enclosing stack. This keeps things organized in your storyboard/XIB file and neatly recalculates constraints for the hidden stacks.
It seems that you want to show some empty (or incomplete) cells until database content arrives and then you will reload each cell as you process new entries in the datasource. This answer will initially give you a set of cells appearing as per your storyboard/XIB, minus the hidden dummy elements. Then as items in your datasource are loaded fully, you can reload the cells.
By the way, it seems like a lot of work to carefully layout these dummy views for "visualization" and then never show them in the app. Why not have some user-friendly place holders or progress indicators showing and then animate in the real/dynamic views as the data arrives?
I assume you download the Image from FireBase and does't want the dummy Image to appear
(Why dont you declare an empty ImageView and an empty Label!). Try setting an Image array in the viewController. Or you can use a struct array if you want a Image and label text together.
var arrImage=[UIImage]()
var arrLblTxt=[String]()
In view did load append your Demo Image's if required.
arrImage.append(UIImage("Demo"))
arrLblTxt.append("Demo")
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tbl_AssignEmployees.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as YourCell
if arrImage[IndexPath.row] != UIImage("Demo")
{
cell.ImageView.Image = arrImage[indexPath.row]
cell.textLabel!.text = arrLblTxt[indexPath.row]
}
else
{
cell.ImageView.Image = nil
cell.textLabel!.text = arrLblTxt[indexPath.row]
}
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsetsZero
cell.layoutMargins = UIEdgeInsetsZero
return cell
}
Then After you download your Image From FireBase,
Reset your Image array with the new Images downloaded from fireBase
Then reload your tableView. tableView.reloadData

How to change subview of uitableviewcell programmatically?

I'm working on app that allows user to share photo like instagram. User can post a photo with or without caption, so the caption could be empty and the photo still posted to their feed. My client want to remove the view when the caption is empty and I'm using uitableview cell with autolayout for the post. When I tried to connect the captionview height constraint to my viewcontroller it gives me an error it says that the constraint outlet can't be connected to repeated content. So, I tried to do it inside my code , but it doesn't change anything.
let captionString = object["caption"] as? String
let captionView = cell.contentView.viewWithTag(111)
if captionString == ""{
captionView?.frame = CGRectMake(0 , 0, captionView!.frame.width, 0)
}else{
(cell.contentView.viewWithTag(12) as! UILabel).text = captionString
}
I put above code inside tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell and I'm giving constraint in every view inside uitableviewcell so if I can change the captionView height other view will automatically resizing. How can I change the height of the captionView?
Related info here:
Outlets cannot be connected to repeating content iOS
You can't set the frame, when you're using autolayout that's invalid and won't work properly.
Either have 2 different styles of cell and choose which one to dequeue based on data availability, or add constraints so that you can collapse the label height to zero.
you can create outlet of height constraint and set the height 0
like as:
#IBOutlet var captionViewHeightConstraint: NSLayoutConstraint!
captionViewHeightConstraint.constant = 0

UICollectionVIew zoom of a single UIImageView in a cell

How to zoom a UIImageView that takes up a fullscreen cell in a UICollectionView image?
Since UICollectionView is a subclass of UIScrollView it should be fairly simple..
I tried to set minimumZoomScale, maximumZoomScale and viewForZoomingInScrollView: but the result is a mess.
In viewForZoomingInScrollView i returned the UIImageView, but that does not work.
Now i'm trying to return the visualized cell, but on zooming the image changes to the first cell.
it zooms but messes up the collection view.
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
if let selectedIndex = self.collectionView.indexPathsForVisibleItems().last as? NSIndexPath
{
let cell = self.collectionView.cellForItemAtIndexPath(selectedIndex)
return cell
}
return nil
}
So, what i need to do to zoom the image (cell) currently on screen?
Moreover pagingEnabled goes off automatically (that's good for panning the zoomed image, but not for scrolling the collection)
You need to put a new UIScrollView inside the cell of your UICollectionView since you should not mess with the zooming of the CollectionView itself.

UICollectionView is not allowing full scroll --- last few rows are cut off

I have a UICollectionView that holds a bunch of a photos.
However, if I scroll to the bottom the scrollview does not let me scroll to the bottom of the last few rows (it snaps back). I have tried override the collectionView.contentSize and just adding 1000 to the height but it doesn't fix the problem.
collectionView.contentSize = CGSizeMake(collectionView.contentSize.width, collectionView.contentSize.height + 1000)
Here is a video of the problem:
https://www.youtube.com/watch?v=fH57_pL0OjQ&list=UUIctdpq1Pzujc0u0ixMSeVw
Here is my code to create cells:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("selectPhotoCell", forIndexPath: indexPath) as B_SelectPhotoControllerViewCell
let reuseCount = ++cell.reuseCount
let asset = currentAssetAtIndex(indexPath.item)
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize:_cellSize, contentMode: .AspectFit, options: nil)//the target size here can be set to CGZero for a super blurry preview
{
result, info in
if reuseCount == cell.reuseCount
{
cell.imageView.image = result
cell.imageView.frame = CGRectMake(0, 0,self._cellSize.width,self._cellSize.height)
}
}
return cell
}
private func currentAssetAtIndex(index:NSInteger)->PHAsset
{
if let fetchResult = _assetsFetchResults
{
return fetchResult[index] as PHAsset
}else
{
return _selectedAssets[index]
}
}
Update:
Because I am adding this as a child view controller, there seems to be some problems with the offsetting of the scrollview. I haven't fixed it yet but when open this view without adding it as a child view to another view controller, the scrollview is the correct size
The problem was I was adding this as a child view controller.
As a result, after doing some animations, the UICollectionView bounds were sizing to the view it was attached to. As a result its height was wrong and hence why it was getting cut off.
I just came across this question from a quick Google and felt I could add something useful.
I am running a segmentedControl with a UIView that changes to different UICollectionViews on the segment change and I couldn't get the collectionView to scroll fully down.
This may not be the solution for all, but I found that if I went to the XIB I was loading in the view and set size to freeform and decrease it by the size of a cell I had removed the problem.
suspect your CollectionView's bottomAchor was not set correctly to the parent uiview's safeAreaLayoutGuide bottomAnchor

Resources