Carousel not displaying in iOS 11.3 (11.2 works fine) - swift

Exact case:
I have a tableViewController presented as a popover in my app.
The first cell in the popover has an iCarousel in it (liner to be precise).
https://github.com/nicklockwood/iCarousel
Implemented all the datasource and delegate functions. They all get fired...
In iOS 11.2 all work as intended, but in iOS 11.3 the popover doesn't show up the iCarousel items. All the delegates still fire (fe. didSelect; didScroll). In the interface debugger mode I can see the items, but not on device.
Tried to reload the data after the initial setup / tried to remove all images from the item and leave it only with pink background / tried to force set alpha to 1 / tried to bringSubviewToFront / eliminated all background effects / modified the tableViewController to a table View and the cell to a view. Nothing works. The most annoying thing is that it shows in the interface debugger and I can't seem to get it working on the actual device.
Popover Presentation:
let storyboard = UIStoryboard(name: "SomeSTBName", bundle: nil)
let popControllerNav = storyboard.instantiateViewController(withIdentifier: "pTVCIdentifier")
let popController = popControllerNav as! PTVC
popController.dissmissDelegate = self
popController.t = t
popController.modalPresentationStyle = UIModalPresentationStyle.popover
popController.preferredContentSize = CGSize(width: 555, height: 120)
popController.presentedFrom = presentedFrom
popControllerNav.popoverPresentationController?.permittedArrowDirections = [.up, .down]
popControllerNav.popoverPresentationController?.backgroundColor = UIColor(red: 17.0/255.0, green: 17.0/255.0, blue: 71.0/255.0, alpha: 1.0)
popControllerNav.popoverPresentationController?.popoverLayoutMargins = UIEdgeInsets.init(top: 0, left: 100, bottom: 0, right: 0)
popControllerNav.popoverPresentationController?.delegate = self
popControllerNav.popoverPresentationController?.sourceView = mySender // button
popControllerNav.popoverPresentationController?.sourceRect = mySender.bounds
if(NViewController.instance?.pressedS != nil) {
popController.popoverPresentationController?.passthroughViews = [(NViewController.instance?.pressedS)!]
}
// present the popover
self.present(popControllerNav, animated: true, completion: nil)
Cell for row in TVC:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var returnCell = UITableViewCell()
if let upperCarousel = tableView.dequeueReusableCell(withIdentifier: "carouselPCell") as? CaruselPCell {
let playlists = CoreDataStack.fetchAllP() as? [UP]
upperCarousel.allP = p
upperCarousel.setup()
upperCarousel.delegate = self
returnCell = upperCarousel
}
return returnCell
}
iCarousel setup:
func setup(){
carusel = iCarousel(frame: CGRect(x: -415, y: 0, width: 950, height: 125))
carusel.scrollSpeed = 0.6
carusel.centerItemWhenSelected = false
carusel.isPagingEnabled = false
carusel.type = .linear
carusel.dataSource = self
carusel.delegate = self
carusel.bounces = false
carusel.reloadData()
carusel.clipsToBounds = true
self.contentView.addSubview(carusel)
}
CGRect(x: -415, y: 0, width: 950, height: 125) because I don't want the first item to be center, but aligned to the left - it doesn't generate the problem had tested

Related

avoiding retain cycles for UITableView done programmatically from a ViewController that is a child of another ViewController

I have a ViewController named MainViewController that acts as the central page for the app. This MainViewController has 6 properties in it that are instances of other viewControllers
lazy var shelvesView: ShelvesViewController = {
return ShelvesViewController()
}()
lazy var goalsView: GoalsViewController = {
return GoalsViewController()
}()
lazy var shoppingView: ShoppingListViewController = {
return ShoppingListViewController()
}()
lazy var tipsView: TipsViewController = {
return TipsViewController()
}()
lazy var myDenView: MyDenViewController = {
return MyDenViewController()
}()
lazy var settingsview: SettingsViewController = {
return SettingsViewController()
}()
when the mainViewController loads it starts out with the shelvesViewController laid over it just underneath the mainViewControllers custom NavBar like so
func setupShelvesView() {
shelvesView.willMove(toParentViewController: self)
addChildViewController(shelvesView)
self.view.addSubview(shelvesView.view)
shelvesView.view.frame = CGRect(x: 0, y: view.frame.height * 0.08, width: view.frame.width, height: view.frame.height - (view.frame.height * 0.08))
shelvesView.didMove(toParentViewController: self)
globalCurrentView = 1
}
I also have a menu that slides over this mainViewController with a list of different pages the user can navigate to. when the user switches to a new page, say the goalsViewController, the shelvesViewController will be animated off screen, removed from the parentViewController(MainViewController) and the goalsViewController will be initialized, moved to the MainViewController and animated onscreen in the same frame as the shelvesView. Anytime i switch a VC from the menu i use this method.
func changeVCfrom(OldVC oldVC: UIViewController, newVC: UIViewController) {
let newStartFrame = CGRect(x: 0 + self.view.frame.width, y: 0, width: view.frame.width, height: view.frame.height - (view.frame.height * 0.08))
let newEndframe = CGRect(x: 0, y: view.frame.height * 0.08, width: view.frame.width, height: view.frame.height - (view.frame.height * 0.08))
let oldfinishFrame = CGRect(x: 0 - self.view.frame.width, y: 0, width: self.view.frame.width, height: self.view.frame.height)
oldVC.willMove(toParentViewController: nil)
self.addChildViewController(newVC)
newVC.view.frame = newStartFrame
transition(from: oldVC, to: newVC, duration: 0.2, options: [.curveEaseOut], animations: {
oldVC.view.frame = oldfinishFrame
newVC.view.frame = newEndframe
}, completion: { (success) in
oldVC.willMove(toParentViewController: nil)
oldVC.view.removeFromSuperview()
oldVC.removeFromParentViewController()
newVC.willMove(toParentViewController: self)
self.view.addSubview(newVC.view)
})
}
the problem here is that i have done all my views programmatically and any of the above viewControllers(lazy properties) that have a tableView propery on them have a memory leak. any time the VC goes off the MainViewController the memory used for the tableView is not being dealocated and anytime it comes back on the MainViewController is is being allocated again for more memory.
as of right now this is how ive been setting up my tableViews
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
setupObjects()
tableView.register(ShelfTableViewCell.self, forCellReuseIdentifier: "shelfCell")
}
func setupTableView() {
view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
setupTableViewConstraints()
}
func setupTableViewConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
}
I understand that I need to have my tableviews either weak, unowned or somehow get the tableview to completely deallocate when off screen. but whatever i try I get errors all over the place that i cant resolve or my ViewController is not able to load the tableview. I apologize for the lengthy question, but any help on this would be very much appreciated.
The table views are not getting deallocated when your child view controllers go off screen.
Your MainViewController owns those other view controllers. Yes, they're lazy, but once they're called for the first time they are initialized along with the tableviews they own.
The child view controllers are not deallocated by simply moving them offscreen. All you're doing is changing the frame and removing them as a child view controller of MainViewController. Since the they're still in memory, the table view is also still in memory.
If you really want to deallocate the table view every time the controller moves off screen, you can just set
self.tableView = nil
There shouldn't be a retain cycle here because the tableView's datasource and delegate properties are weak, meaning that they don't increase the reference count of the view controller.
while scrolling up and down on the tableView my memory would go up sharply. Turns out i had set up the dequeReusableCell wrong.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ShelfTableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "shelfCell")
tableView.dequeueReusableCell(withIdentifier: "shelfCell", for: indexPath)
if let shelf = UserController.shared.user?.shelves?[indexPath.row] as? Shelf {
cell.shelf = shelf
return cell
} else {
return UITableViewCell()
}
}
essentially what was happening was every time the viewWillAppear was hit i reloaded the tableViewData which ran this code
let cell = ShelfTableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "shelfCell")
tableView.dequeueReusableCell(withIdentifier: "shelfCell", for: indexPath)
which was causing the memory leak
replacing the above code with the following resolved the issue
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "shelfCell", for: indexPath) as! ShelfTableViewCell
if let shelf = UserController.shared.user?.shelves?[indexPath.row] as? Shelf {
cell.shelf = shelf
return cell
} else {
return UITableViewCell()
}
}

Adding shadow to tableview cell cause laggy scroll and duplicate shadow

I have added white space around my tableview cell and every time I scroll this shadows getting bigger and bigger when I scroll, and its get lag when I scroll for the second and third time with the bigger shadow
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
cell.backgroundColor = UIColor.clear
cell.contentView.backgroundColor = UIColor.clear
let whiteRoundedView : UIView = UIView(frame: CGRect(x:10,y: 5,width: self.view.frame.size.width - 20,height: 117))
whiteRoundedView.layer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 1.0, 0.9])
//whiteRoundedView.layer.masksToBounds = true
whiteRoundedView.layer.cornerRadius = 5.0
whiteRoundedView.layer.shadowOffset = CGSize(width: -1,height: 1)
whiteRoundedView.layer.shadowOpacity = 0.2
let shadowPath = UIBezierPath(rect: whiteRoundedView.layer.bounds)
whiteRoundedView.layer.shouldRasterize = true
whiteRoundedView.layer.shadowPath = shadowPath.cgPath
cell.contentView.addSubview(whiteRoundedView)
cell.contentView.sendSubview(toBack: whiteRoundedView)
return cell
}
Just put code inside the awakefrom nib
class CustomCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.backgroundColor = UIColor.clear
self.contentView.backgroundColor = UIColor.clear
let whiteRoundedView : UIView = UIView(frame: CGRect(x:10,y: 5,width: self.contentView.frame.size.width - 20,height: 117))
whiteRoundedView.layer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 1.0, 0.9])
//whiteRoundedView.layer.masksToBounds = true
whiteRoundedView.layer.cornerRadius = 5.0
whiteRoundedView.layer.shadowOffset = CGSize(width: -1,height: 1)
whiteRoundedView.layer.shadowOpacity = 0.2
let shadowPath = UIBezierPath(rect: whiteRoundedView.layer.bounds)
whiteRoundedView.layer.shouldRasterize = true
whiteRoundedView.layer.shadowPath = shadowPath.cgPath
self.contentView.addSubview(whiteRoundedView)
self.contentView.sendSubview(toBack: whiteRoundedView)
}
}
You keep adding shadow views on top of each other without ever removing them. If all your cells will need the shadow you can just add a tag and check if a view with that tag already exists like so:
whiteRoundedView.tag = 12345
if cell.contentView.viewWithTag(12345) == nil {
cell.contentView.addSubview(whiteRoundedView)
}
If some cells have the shadow but some cells don't you could do it like this:
if let shadowView = cell.contentView.viewWithTag(12345) && noShadow {
shadowView.removeFromSuperview
} else if !noShadow {
cell.contentView.addSubview(whiteRoundedView)
}
Alternatively like mentioned in the comments of the question you would add it to your custom cell class:
override func awakeFromNib() {
super.awakeFromNib()
let whiteRoundedView : UIView = UIView(frame: CGRect(x:10,y: 5,width: self.contentView.frame.size.width - 20,height: 117))
whiteRoundedView.layer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 1.0, 0.9])
whiteRoundedView.layer.cornerRadius = 5.0
whiteRoundedView.layer.shadowOffset = CGSize(width: -1,height: 1)
whiteRoundedView.layer.shadowOpacity = 0.2
let shadowPath = UIBezierPath(rect: whiteRoundedView.layer.bounds)
whiteRoundedView.layer.shouldRasterize = true
whiteRoundedView.layer.shadowPath = shadowPath.cgPath
self.contentView.addSubview(whiteRoundedView)
}
You are adding whiteRoundedView every time. If you are using a storyboard or Nib for designing your Cell UI then you can add whiteRoundedView there Or you can add a tag in whiteRoundedView and check if there are already any view added with that tag before adding the whiteRoundedView as subview.

TableView in iCarousel finding nil while unwrapping optional value

So, I have a carousel of "BillSplitters" and on each carousel item it should display the uniques items a BillSplitter is having. So I'm getting fatal error: unexpectedly found nil while unwrapping an Optional value Normally i can slowly hone in on an error like this i find the issue but when following on from a breakpoint line by line it enters into the iCarousel code which i cant follow. Im also sure theres nothing going wrong in i carousel as if i dont addSubview(tableView) then it runs fine. It also seems to create the first couple of tableviews and add them fine and then gets the error. Here is the code im using:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let splitter = allBillSplitters[carouselIndex]
if (splitter.items?.count)! > 0 {
return (splitter.items?.count)!
} else {
TableViewHelper.EmptyMessage("\(splitter.name!) has no items to pay for.\nGo back to assign some items to their name.", tableView: tableView)
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ItemCell = tableView.dequeueReusableCell(withIdentifier: "SplitterItemCell") as! ItemCell
let itemsSet = allBillSplitters[carouselIndex].items
let items = itemsSet?.allObjects as! [Item]
let item = items[indexPath.row]
let count = item.billSplitters?.count
if count! > 1 {
cell.name!.text = "\(item.name!) split \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
func numberOfItems(in carousel: iCarousel) -> Int {
return allBillSplitters.count
}
I've read in a few places that I should remove the if let view = view statement in the following function as it's not re-using the items and always creating new ones. If I leave it in I get the same error immediately when creating the first carousel item and when I remove it, it happens on the creating the third i carousel item.
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
carouselIndex = index
var splitterView: UIImageView
var nameLabel: UILabel
var emailLabel: UILabel
var totalLabel: UILabel
var tableView: UITableView
let splitter = allBillSplitters[index]
//reuse view if available, otherwise create a new view
if let view = view as? UIImageView {
splitterView = view
//get a reference to the label in the recycled view
nameLabel = splitterView.viewWithTag(1) as! UILabel
emailLabel = splitterView.viewWithTag(2) as! UILabel
totalLabel = splitterView.viewWithTag(3) as! UILabel
tableView = splitterView.viewWithTag(4) as! UITableView
} else {
let height = carousel.contentView.frame.height - 85
let width = carousel.contentView.frame.width - 80
//don't do anything specific to the index within
//this `if ... else` statement because the view will be
//recycled and used with other index values later
splitterView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
splitterView.layer.cornerRadius = 10
splitterView.clipsToBounds = true
splitterView.image = UIImage(data: splitter.image as! Data, scale:1.0)
splitterView.contentMode = .scaleAspectFit
splitterView.backgroundColor = UIColor(netHex: 0xCA9875)
let viewWidth = Int(splitterView.frame.width)
nameLabel = UILabel(frame: CGRect(x: 5, y: 0, width: viewWidth, height: 30))
nameLabel.backgroundColor = .clear
nameLabel.backgroundColor?.withAlphaComponent(0.1)
nameLabel.textAlignment = .left
nameLabel.font = nameLabel.font.withSize(20)
nameLabel.tag = 1
emailLabel = UILabel(frame: CGRect(x: 5, y: 30, width: viewWidth, height: 15))
emailLabel.backgroundColor = .clear
emailLabel.textAlignment = .left
emailLabel.font = emailLabel.font.withSize(15)
emailLabel.tag = 2
totalLabel = UILabel(frame: CGRect(x: 5, y: 45, width: viewWidth, height: 15))
totalLabel.backgroundColor = .clear
totalLabel.textAlignment = .left
totalLabel.font = totalLabel.font.withSize(15)
totalLabel.tag = 3
let tableViewHeight = height - 65
let frame = CGRect(x: 0, y: 65, width: width, height: tableViewHeight)
tableView = UITableView(frame: frame)
tableView.delegate = self
tableView.dataSource = self
tableView.tag = 4
totalLabel.backgroundColor = .clear
splitterView.addSubview(nameLabel)
splitterView.addSubview(emailLabel)
splitterView.addSubview(totalLabel)
splitterView.addSubview(tableView)
}
//set item label
//remember to always set any properties of your carousel item
//views outside of the `if (view == nil) {...}` check otherwise
//you'll get weird issues with carousel item content appearing
//in the wrong place in the carousel
nameLabel.text = "\(allBillSplitters[index].name!)"
emailLabel.text = "\(allBillSplitters[index].email!)"
totalLabel.text = "£\(allBillSplitters[index].total)"
return splitterView
}
func carousel(_ carousel: iCarousel, valueFor option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
switch option {
case .spacing:
return value * 1.2
case .fadeMin:
return 0.0
case .fadeMinAlpha:
return 0.3
case .fadeMax:
return 0.0
default:
return value
}
}
I've looked all over and can't find a solution so any help would be great. Thanks
I'm an idiot. Forgot the following:
tableView.register(CarouselTableViewCell.classForCoder(), forCellReuseIdentifier: "carouselTableViewCell")
in tableviews cellForRowAt function

How to apply custom styling to NSTableHeaderView?

So I am going for a custom looking NSTableView. I've already successfully subclassed NSTableRowView and NSTextFieldCell to achieve the look I'm going for, however I'm struggling of getting rid of the default styling for the header. I seem to be able to tweak its frame, however I'm not sure where the rest of the default styling is coming from.
As you see on the screenshot the red area is the increased frame of the headerView. I'm using its CALayer to set the colour, however how to change the contents inside is beyond me...
Here's what I'm doing in the viewDidLoad of my ViewController
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.wantsLayer = true
tableView.headerView?.frame = NSMakeRect(0, 0, (tableView.headerView?.frame.width)!, 32.00)
tableView.headerView?.wantsLayer = true
tableView.headerView?.layer?.backgroundColor = NSColor.red.cgColor
}
I've also tried subclassing NSTableHeaderView, however this class seems to be extremely limited in terms of the customizations I can make...
Any help would be appreciated?
The table view is view based but the header isn't and the header cells still are class NSTableHeaderCell. Use NSTableColumn's property headerCell. You can set the cell's properties like attributedStringValue and backgroundColor or replace the cells by instances of a subclass of NSTableHeaderCell and override one of the draw methods.
Play around with this to get inside the header.
Remember to except the answer if it works for you.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//Color for the header.
let topColor = UIColor(red: (70/255.0), green: 000/255.0, blue: 000/255.0, alpha: 255)
//Location of label.
let locationOfLabel = self.view.frame.width
let headerView:UIView = UIView()
//Locating the text in the label
let title = UILabel(frame: CGRect(x: 0, y: 30, width: locationOfLabel, height: 21))
title.textAlignment = .center
//Changing the title in the label per the default.
let defaults:UserDefaults = UserDefaults.standard
defaults.synchronize()
let cardSelector = defaults.object(forKey: "selectorKeyID") as! Int
switch (cardSelector) {
case 0: title.text = "Personal"
break
case 1: title.text = "Saved"
break
case 2: title.text = "Favorite"
break
case 3: title.text = "Grouped"
break
default:
break
}
//Coloring the text in the label
//Add the label
title.textColor = UIColor.gray
headerView.addSubview(title)
//Adding a button to the header.
let closeBttn = UIButton(type: UIButtonType.system) as UIButton
closeBttn.frame = CGRect(x: 0, y: 30, width: 90, height: 27)
closeBttn.setTitle("Close", for: UIControlState())
closeBttn.setTitleColor(buttonColor, for: UIControlState())
closeBttn.titleLabel?.font = UIFont.systemFont(ofSize: 19, weight: UIFontWeightMedium)
closeBttn.addTarget(self, action: #selector(MainTableViewController.close), for: UIControlEvents.touchUpInside)
headerView.addSubview(closeBttn)
let menuButton = UIButton(type: UIButtonType.system) as UIButton
menuButton.frame = CGRect(x: locationOfLabel-53, y: 30, width: 27, height: 27)
menuButton.setBackgroundImage(UIImage(named: "VBC Menu4.png"), for: UIControlState())
menuButton.addTarget(self, action: #selector(MainTableViewController.menuButton), for: UIControlEvents.touchUpInside)
headerView.addSubview(menuButton)
//Coloring the header
headerView.backgroundColor = topColor
//Rounding the corners.
headerView.layer.cornerRadius = 10
headerView.clipsToBounds = true
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}

UICollectionView cell views overlapping

I have cells overlapping like so:
my cellForItemAtIndexPath is as such:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as UICollectionViewCell
cell.backgroundColor = UIColor(red: 27.0/255.0, green: 38.0/255.0, blue: 52.0/255.0, alpha: 1.0)
let textFrame = CGRect(x: 0, y: cell.frame.height * 0.30, width: cell.frame.width, height: cell.frame.height)
var textLabel: UILabel! = UILabel(frame: textFrame)
textLabel.font = UIFont(name:"Helvetica-Light", size: 14.0)
textLabel.textAlignment = .Center
textLabel.textColor = UIColor.whiteColor()
println(categoryArray[indexPath.row].category)
textLabel.text = categoryArray[indexPath.row].category
var cellImage = UIImage(named: categoryArray[indexPath.row].catImage)//Array(Array(model.categories.values)[cellCount])[1]
let imageSize = cell.frame.height * 0.45
let imageView = UIImageView(image: cellImage as UIImage?)
imageView.frame = CGRect(x: (cell.frame.width / 2) - (imageSize / 2), y:cell.frame.height * 0.15, width: imageSize, height: imageSize)
var bottomBorder: UIView = UIView(frame:CGRectMake(0, cell.frame.height - 1.0, cell.frame.width, 5));
//bottomBorder.backgroundColor = UIColor(rgba: Array(Array(model.categories.values)[cellCount])[0] as String)
bottomBorder.backgroundColor = UIColor(rgba: "#A64259")
cell.addSubview(imageView)
cell.addSubview(bottomBorder)
cell.addSubview(textLabel)
cellCount++
return cell
}
I understand that it reuses the cells, great idea...except how do I prevent the cell text from overlapping?
EDIT - POTENTIAL SOLUTION #1
Since these subviews were continually being modified I figured, what if I just dumped them and created new ones so I used:
for view in cell.subviews {
view.removeFromSuperview()
}
And that seemed to do the trick. I suspect this has a little more overhead than just modifying the values of the specific elements in the subviews. Will investigate further.
The reason it's happening is because the cells are being reused and you end up adding the image as a subview multiple times to the same UICollectionViewCell object. You can make a custom class that extends UICollectionViewCell so that you can hold onto the imageView that you add.
class ImageCell : UICollectionViewCell {
private(set) var imageView : UIImageView?
private(set) var textLabel : UILabel?
func setImage(image: UIImage?) {
if self.imageView == nil {
let imageSize = cell.frame.height * 0.45
self.imageView = UIImageView()
self.imageView.frame = CGRect(x: (self.frame.width / 2) - (imageSize / 2), y:self.frame.height * 0.15, width: imageSize, height: imageSize)
self.addSubview(imageView!)
}
imageView!.image = image
}
func setLabel(text: String) {
if self.textLabel == nil {
let textFrame = CGRect(x: 0, y: self.frame.height * 0.30, width: self.frame.width, height: self.frame.height)
self.textLabel = UILabel(frame: textFrame)
textLabel.font = UIFont(name:"Helvetica-Light", size: 14.0)
textLabel.textAlignment = .Center
textLabel.textColor = UIColor.whiteColor()
}
textLabel.text = text
}
}
Then in your cellForItemAtIndexPath:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as ImageCell
var cellImage = UIImage(named: categoryArray[indexPath.row].catImage)
cell.setImage(cellImage)
cell.setLabel(categoryArray[indexPath.row].category)
Obviously you would have to customize it to get the same layout, but that should get you started.
Well, since it reuses the cell, and since you are adding subviews to the cell on every call, you will end up with multiple overlapping views in the same cell!
Instead, you may want to add the subviews only once, tag them, then on getting called to provide a cell dequeue one, retrieve the subviews using their tags, and set their properties as needed.