I have an app with a screen that is divided into four equal cells. Each cell has an image, label, and color. I'm trying to add the images in, but for some reason, only the image in the first cell works.
It looks like this now:
Here is my code:
In ViewController.swift:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
colorArray += [UIColor.red, UIColor.blue, UIColor.green, UIColor.yellow]
pictureArray += [UIImage(named: "budget")!, UIImage(named: "journey")!,
UIImage(named: "learn")!, UIImage(named: "settings")!]
titleArray += ["Budget", "Journey", "Learn", "Settings"]
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as UICollectionViewCell
// Set cell properties
cell.backgroundColor = colorArray[indexPath.row]
let imageview:UIImageView=UIImageView(image: pictureArray[indexPath.row])
let size = CGSize(width: 100, height: 100)
imageview.center = cell.center
imageview.bounds = CGRect(origin: cell.bounds.origin, size: size)
cell.contentView.addSubview(imageview)
let label = cell.viewWithTag(1) as! UILabel
label.text = titleArray[indexPath.row]
return cell
}
The labels and colors work fine, but for some reason, the image views seem to be off the screen. I have a feeling that my center/bounds restrictions are forcing the images off the screen, but I've tried many combinations, and none of them seem to work for me.
How should I do this?
I'm posting the correct answer for who ever will google here. (And because it was solved an hour ago without anyone posting the answer)
The best practice is to subclass UICollectionViewCell, connect all the outlets (label, image etc.) and work with this class on the cells
Good luck to all
This is what worked, after Yogesh Suthar's comment:
I subclassed UICollectionViewCell, connected the image and label:
class NavigationCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var navImage: UIImageView!
#IBOutlet weak var navLabel: UILabel!
}
Then, in the ViewController, I did this:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NavigationCollectionViewCell
// Set cell properties
cell.backgroundColor = colorArray[indexPath.row]
cell.navImage.image = imageArray[indexPath.row]
cell.navLabel.text = titleArray[indexPath.row]
return cell
}
Set the size and position of the image and label in Main.storyboard!
insert the desired view as a subclass and setup all constrains to cell.heightAchor and so on. this will fix the bug for some reason. It works if the subview is added to the cell as a subview but not as an attribute of the cell
Related
I have a collectionview with various cells. One can be selected at once and the selection state is stored inside my business logic. If a cell is selected the whole section get's reloaded to update the UI to highlight the currently selected cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
cell.setItemSelected(selected: selected)
return cell
}
My problem is that a random cell get's highlighted to for a short time.
I also implemented prepareForReuse() but without any effect.
What can cause this?
Thanks!
Here the requested additional code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let pendencyDeficiencyType = deficiencyTypes[indexPath.row]
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PendencyDetailCell.reuseIdentifier, for: indexPath) as? PendencyDetailCell{
cell.titleLabel.attributedText = StringUtils.hyphenedText(pendencyDeficiencyType.rawValue)
cell.indexPath = indexPath
cell.delegate = self
let selected = pendencyDeficiencyType == pendencyItem?.pendencyDeficiencyType
cell.setItemSelected(selected: selected)
return cell
}
return collectionView.dequeueReusableCell(withReuseIdentifier: PendencyDetailCell.reuseIdentifier, for: indexPath)
}
override func prepareForReuse() {
super.prepareForReuse()
backgroundColor = Styling.cellDefaultColor
titleLabel.textColor = .black
}
func setItemSelected(selected: Bool){
layer.borderColor = selected ? Styling.heagPrimary.cgColor : UIColor.darkGray.cgColor
backgroundColor = selected ? Styling.cellSelectedColor : Styling.cellDefaultColor
titleLabel.textColor = selected ? .white : .black
}
As an option you can implement two separate cells.. one for each state and use it depending on the flag.
OR
Try to use different ReuseIdentifiers depending on the flag.
Currently, I have embedded a UICollectionViewCell in a UITableViewCell within one of the sections of my UITableView. I know how to dynamically change the cell's height in another section of my UITableView because I have a UITextView in another UITableViewCell that dynamically changes the height of the cell based on how much text is in the UITextView.
The problem I have is in regards to the UITableViewCell containing the UICollectionViewCell. My UICollectionViewCell has one row of 4 images that the user can add via the camera or photo library using a UIImagePickerController.
Currently as I have it, when the 5th picture is generated, the UITableViewCell's height remains static, but the user can scroll horizontally in the UICollectionViewCell like so:
My end goal is this:
And my storyboard:
Pretty self-explanatory but if there is only 4 images, the UITableViewCell remains the same as in screenshoot 1, but the cell's height will dynamically change if the UICollectionViewCell's height changes.
I have set the UICollectionView's scroll direction to be vertical only. Before explaining further, here's my partial code:
class TestViewController: UITableViewController, UITextFieldDelegate, UITextViewDelegate, UIImagePickerControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
....
tableView.estimatedRowHeight = 40.0
tableView.rowHeight = UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell: UITableViewCell = UITableViewCell()
if indexPath.section == 1
{
cell = tableView.dequeueReusableCell(withIdentifier: "TextViewCell", for: indexPath)
let textView: UITextView = UITextView()
textView.isScrollEnabled = false
textView.delegate = self
cell.contentView.addSubview(textView)
}
else if indexPath.section == 4
{
if let imagesCell = tableView.dequeueReusableCell(withIdentifier: "ImagesCell", for: indexPath) as? CustomCollectionViewCell
{
if images_ARRAY.isEmpty == false
{
imagesCell.images_ARRAY = images_ARRAY
imagesCell.awakeFromNib()
}
return imagesCell
}
}
return cell
}
....
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
if indexPath.section == 1
{
return UITableViewAutomaticDimension
}
else if indexPath.section == 4
{
//return 95.0
return UITableViewAutomaticDimension
}
return 43.0
}
....
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage
{
if let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 4) ) as? CustomCollectionViewCell
{
cell.images_ARRAY.append(selectedImage)
cell.imagesCollectionView.reloadData()
}
}
picker.dismiss(animated: true, completion: nil)
}
....
func textViewDidChange(_ textView: UITextView)
{
...
// Change cell height dynamically
tableView.beginUpdates()
tableView.endUpdates()
}
}
class CustomCollectionViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
#IBOutlet var imagesCollectionView: UICollectionView!
var images_ARRAY = [UIImage]()
var images = [INSPhotoViewable]()
override func awakeFromNib()
{
super.awakeFromNib()
for image in images_ARRAY
{
images.append(INSPhoto(image: image, thumbnailImage: image) )
}
imagesCollectionView.dataSource = self
imagesCollectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return images_ARRAY.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! ExampleCollectionViewCell
cell.populateWithPhoto(images[(indexPath as NSIndexPath).row]
return cell
}
....
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return UIEdgeInsetsMake(0.0, 25.0, 0.0, 25.0)
}
}
Originally, my indexPath.section == 4, which contains the UICollectionViewCell returned a height of 95, but I commented that out and replaced it with returning UITableViewAutomaticDimension. I would assume that adjusted the height of the cell to fit the 5th image, but the cell remained a static height even though the UICollectionViewCell' height changed, allowing me to scroll vertically within that static UITableViewCell height.
I know these are some questions I found very similar to my situation, but they didnt help me resolve my particular issue:
Swift: Expand UITableViewCell height depending on the size of the
UICollectionView inside it
Auto Height of UICollectionView inside UITableViewCell
UICollectionView inside a UITableViewCell — dynamic height?
With some of the answers and suggestions, I've added the following:
imagesCell.images_ARRAY = images_ARRAY
imagesCell.awakeFromNib()
// Added code
imagesCell.frame = tableView.bounds
tableView.setNeedsLayout()
tableView.layoutIfNeeded()
However, this did not have any effects. Can anyone point me in the right direction on what code I need and placed where?
Thanks!
I am using these type of cells in my code, Not performing excellent performance wise(as affecting scrolling smoothness) but will let you achieve required design.
Use CollectionView inside tableViewCell with Vertical ScrollDirection and fixed width(I mean not dynamic in nature). This will put overflowing cells in vertical direction after filling horizontal direction.
Take out NSLayoutConstraint from xib(if you are using that) of collectionViewHeight. We will use it in later part.
set UITableViewAutomaticDimension in tableView in heightForRowAtIndexPath method.
And finally set cell's collectionViewHeight while returning cell in cellForRowAtIndexPath method using constraint that we took out in step 2.
Here I am attaching some code that may will help:
UITableView Part:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: xyzTableViewCell.self), for: indexPath) as! xyzTableViewCell
cell.collectionViewHeight.constant = (numberOfCells/5) * cell.cellHeight
return cell
}
UITableViewCell Part:
#IBOutlet fileprivate weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint
And you will need to reload that particular tableViewCell and reload collectionView inside this tableViewCell so that height function of tableView will be called and height of that tableViewCell will be refreshed, and to handle focused condition of that tableViewCell(when tableViewCell is in focus), I am saying this because if it's not in focus(or say cache, there is difference between them though) then cellForRowAtIndexPath method will be called on scrolling(when this cell is going to come in focus) then tableViewCell height will already be taken care of.
Hope this will help to achieve required functionality.
I am new to iOS development and am trying to create a PokeDex app. My main view controller has a collection view which returns two types of custom cells. My first cell called the chooser cell is immediately underneath the navigation bar or header bar whichever term, and I want my second custom cell(descriptioncell) to be immediately underneath the choosercell and take up the entire screen. I originally set the height of the description cell to be the height of the view - 50 to take the chooser cell into account but I saw that there was some space left over which shows the color of the collectionview. I then decided to make the descriptionview the height of the entire view.frame but there is still empty space for some reason. It is not a lot of space and by making the collectionview the same color as my descriptioncell everything would look fine, but why is this happening?
code in viewcontroller:
import UIKit
class PokeDexController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
private let cellID = "cellID"
private let cellID2 = "cellID2"
override func viewDidLoad() {
super.viewDidLoad()
self.title = "PokeDex 386"
//collectionView?.backgroundColor = UIColor(red: 52/255.0, green: 55/255.0, blue: 64/255.0, alpha: 1.0)
collectionView?.backgroundColor = UIColor.white
collectionView?.register(chooserCell.self, forCellWithReuseIdentifier: cellID)
collectionView?.register(DescriptionCell.self, forCellWithReuseIdentifier: cellID2)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if (indexPath.row == 0)
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! chooserCell
return cell
}
else
{
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: cellID2, for: indexPath) as! DescriptionCell
return cell2
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if(indexPath.row == 0)
{
return CGSize(width: view.frame.width, height: 50)
}
else
{
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
}
how the app currently looks, empty space between two cells
Thanks
I would double check view.frame to make sure it is what you expect it to be, just to rule out something simple. More times that I'd like to admit i've used references to another view who's frame was not what I thought it was going to be.
Another thing to check would be the content insets/spacing of the UICollectionView especially if you're using storyboards, I don't know if storyboards set a default inset/cell spacing or not. If you always seem to have some spacing around the edges of your cells its most likely some padding/spacing configured on the UICollectionView itself.
Also a likely culprit can be AutoLayout constraints, by default storyboards attach to layout margins not the layout edges. The margins are inset slightly from the true edge creating a gap.
Hopefully that helps some or at least gets you looking in the right places
I am trying to use a UICollectionView to display a square MyCollectionViewCell that has animated GIFs in a UIImageView.
Rows and sections are setup like so:
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
I'm using SwiftGif to get GIFs assigned to the cell's UIImageView like so:
let gifs = [UIImage.gifWithName("gif1"), UIImage.gifWithName("gif2"), UIImage.gifWithName("gif3")]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
cell.gifImageView.image = gifs[indexPath.item % gifs.count]
return cell
}
Everything for the most part works great. But my issue is that when scrolling, there are times when cell is blank and no GIF appears. In the debugging process, I've added a label to the cell to display the indexPath.item, as you can see in code above, to make sure that the cell isn't getting passed over and have found that the label will always display indexPath even if the cell does not display a GIF.
I have tested with regular images instead like so:
let testImages = [UIImage(named: "testImage1"), UIImage(named: "testImage2", UIImage(named: "testImage3")]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
cell.gifImageView.image = testImages[indexPath.item % testImages.count]
return cell
}
and had no occurrences of blank cells.
Even more curious, when I originally actually built the GIF in collectionView(...cellForItemAtIndexPath) I did not get any issues with blank cells either:
let gifNames = ["gif1", "gif2", "gif3"]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
let gif = UIImage.gifWithName(gifNames[indexPath.item % gifNames.count])
cell.gifImageView.image = gif
return cell
}
This original implementation would have worked if it weren't for the fact the GIF build process drastically affects the scrolling performance of the UICollectionView which is what forced me to change implementation in the first place.
I have confirmed that this is not an issue with SwiftGif as I have replicated this issue in a different application using an animation render library and an AnimatedView in place of the UIImageView in MyCollectionViewCell and displayed animations instead of GIFs and got the same issue with cells randomly showing nothing instead of the animation when scrolling through the CollectionView.
I have tried the StackOverflow solution here and implemented a custom UICollectionViewFlowLayout like so:
class MyFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElementsInRect(rect)
let contentSize = self.collectionViewContentSize()
let newAttrs = attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY <= contentSize.height}
return newAttrs
}
}
And assigned it in viewDidLoad() like so:
self.collectionView?.collectionViewLayout = MyFlowLayout()
In MyFlowLayout I have also tried:
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
I have also messed with various cell sizes (width 1 less than height, height 1 less than width etc) and messed around with some section inset combinations but have not managed to find the source of this issue that is causing the animated view to not to show up.
Any help would be appreciated. Thanks
I solved the issue by setting the gifImageView.image = nil in collectionView(...cellForItemAtIndexPath) before assigning the image.
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("gifCell", forIndexPath: indexPath) as! GifCollectionViewCell
cell.gifImageView.image = nil // Added this line
cell.gifImageView.image = gifs[indexPath.item % gifs.count]
cell.infoLabel.text = String(indexPath.item)
return cell
}
Not sure how/why this fixed it but it works now.
I got some serious problems with my collectionview.
So I fetch async. some data from an api and set the data variable, than I reload my collectionview. My collectionview contains basicly some cells with data from the api and one cell extra with a plus icon. The cells shape and layout is the same, so I used the same collectionviewcell class for both.
I calculate my number of items like this:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count + 1
}
And do my cell stuff like this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! OverviewUserCell
if(indexPath.row == data.count) {
cell.userImageView.image = UIImage(named: "plus")!
//some additional coloring and stuff
} else {
cell.userImageView.image = UIImage(named: "oma")!
//some additional coloring and stuff
}
cell.contentView.frame = cell.bounds
cell.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
cell.layer.cornerRadius = 8
return cell
}
My cell itself contains a round image and a Label:
class OverviewUserCell: UICollectionViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var userImageView: UIImageView!
override func layoutSubviews() {
super.layoutSubviews()
self.makeItCircle()
}
func makeItCircle() {
self.userImageView.layer.masksToBounds = true
self.userImageView.layer.cornerRadius = CGFloat(roundf(Float(self.userImageView.frame.size.height/2.0)))
}
}
Somehow (maybe because different image sizes) the frame of the imageview changes, or something like this, because the plus image is not round, but I want it to stay the same. I added constraints to every element in the cell. If I don't load the data, the image is perfectly round, but when the data is loaded and the collectionview reloaded, the plus image is not round anymore.
The images should be same sized and same position.
Instead of setting corner radius of cell, try setting corner radius of UIImageView. But before that make sure that your UIImage view should have height equal to width.
And before setting imageview.layer.cornerRadius = 8;
add this line: imageview.layoutIfNeeded();