Why can't I change custom view background in cell of UICollectionView - swift

I have a UICollectionView and a custom UICollectionViewCell.
The UICollectionViewCell contains a custom UIView.
I need to change the color of the UIView according to a property in my array.
I have created the custom UIView in the storyboard, assigned appropriate class to it and even a tag.
I am trying to change the color of the my custom circleView in cellForItemAtIndexPath
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("circleCell", forIndexPath: indexPath) as! BRGameCollectionViewCell
var circle = BRGameManager.sharedInstance.gameArray[indexPath.section][indexPath.row] as BRCircle!
cell.circleView.circleColor = UIColor.redColor()
return cell
}
I have tried it even with getting the circleView via tag but it doesn't work either.
What Am I doing wrong?
My custom view Class looks like this:
class BRCircleView: UIView {
var circleSize : CGFloat = 10
var circleColor : UIColor!
override init(frame: CGRect) {
if( frame == CGRectZero){
var newFrame = CGRectMake(0, 0, self.circleSize, self.circleSize)
}
self.circleColor = UIColor(rgba: GlobalConstants.colors.hexColorGreen)
super.init(frame: frame)
}
convenience init(frame: CGRect, circleColor : UIColor){
self.init(frame: frame)
self.circleColor = circleColor
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func drawRect(rect: CGRect) {
self.layer.cornerRadius = self.frame.size.width/2
self.clipsToBounds = true
self.backgroundColor = self.circleColor
}
}

If you want to reflect your circleView's background color change,you need to redraw your view's content.Use setNeedsDisplay method of UIView would do this trick.More about setNeedsDisplay
So before returning cell, add this line of code.
...
cell.circleView.circleColor = UIColor.redColor()
cell.circleView.setNeedsDisplay() // add this line.
return cell

Related

Reusable UICollectionViewCell not changing UIImageView property (showing duplicates)

I have a UICollectionView with a reusable cell that contains a UIImageView but the collection view displays the same image in the image view for each cell item in the collection.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let path = indexPath.item
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "friendAvatar", for: indexPath) as! AvatarCollectionViewCell
cell.avatarView.image = friends[path].userAvatar
return cell
}
class AvatarCollectionViewCell: UICollectionViewCell {
var avatarView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
prepareForReuse()
config()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
avatarView.image = nil
avatarView.removeFromSuperview()
}
func config() {
avatarView.frame = CGRect(x: 0, y: 0, width: 64, height: 64)
avatarView.layer.cornerRadius = 64 / 4
avatarView.clipsToBounds = true
avatarView.contentMode = .scaleAspectFill
addSubview(avatarView)
}
}
First of all, you are removing the image view from its superview in prepareForReuse() but you never add it back. init() will not be called when reusing a cell, and neither will config(). Unless you have a specific reason for it, you shouldn't need to remove the image view from its superview. Just setting its image to nil should be enough.
Also, set a breakpoint and verify that friends[path].userAvatar returns a valid path to an image.

Complex Image based Button Layout

I'm fairly new to programming in general, I'm trying to figure out how to create numerous buttons in a complex layout. My layout is basically a transparent PNG of a body cut into about 24 sections and I want each segment of the body to be a separate button.
I've tried a few layouts in the view controller. Setting up a ton of buttons overlaying an image (couldn't keep the layout straight when launching in the simulator) and I've tried giving the buttons images, I've tried large image sized buttons but I could only use the top most button.
Is there any way to do this, or is it going to need code to be doable?
UICollectionViewController or UICollectionView is a better choice for you. You can set the UICollectionViews background image and regard the cells as your buttons.
Here is a quick example.
import UIKit
class Collection: UICollectionViewController {
private let cellId = "cell"
override init(collectionViewLayout layout: UICollectionViewLayout) {
if let l = layout as? UICollectionViewFlowLayout{
l.minimumInteritemSpacing = 0
l.sectionInset = UIEdgeInsetsZero
l.scrollDirection = .Vertical
l.footerReferenceSize = CGSize.zero
l.headerReferenceSize = CGSize.zero
let screenWidth = UIScreen.mainScreen().bounds.width
let itemWidth = CGFloat(Int(screenWidth/4))
l.itemSize = CGSize(width: itemWidth, height: itemWidth)
}
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
}
extension Collection{
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 4
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellId, forIndexPath: indexPath)
cell.backgroundColor = UIColor.redColor()
return cell
}
}
If you want to add touch action to a cell.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
}

Set text color of all labels in a table view that has a bunch of static cells

I have a Settings screen that is a UITableViewController and contains round 20 static(!) cells (4 groups, 5 cells in each group). Every static cell contains a label.
Is there a way to set the text color of all labels without creating an outlet for each label and setting its text color individually?
Here is one more way to do this. This one guarantees that you access all the labels in the cell's view hierarchy, does not matter at what level they are:
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
recursiveSetTextColorForLabelsInView(cell)
}
func recursiveSetTextColorForLabelsInView(inView: UIView) {
for view in inView.subviews {
if let subview = view as? UILabel {
subview.textColor = UIColor.redColor()
}
else {
self.recursiveSetTextColorForLabelsInView(view)
}
}
}
Alternatively implement -tableView:cellForRowAtIndexPath:, just don't call dequeueReusableCellWithIdentifier: as that doesn't work on static tableView cells. Call super.cellForRowAtIndexPath: instead.
Then you can access the label via cell.textLabel or if custom cell: cell.contentView.subviews.first as? UILabel
I would recommend subclassing UILabel, and setting your text color in the subclass. In your layout, change the class of your UILabels to your subclass. As a bonus, make it IBDesignable so you can see your customizations show up in the storyboard.
import UIKit
#IBDesignable
class CustomLabel: UILabel {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
customize()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func prepareForInterfaceBuilder() {
customize()
}
private func customize() {
textColor = UIColor.redColor()
}
}
Here is how I did it:
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let view = cell.contentView.subviews.first
if let viewToTest = view as? UILabel
{
viewToTest.textColor = UIColor.redColor()
}
}
Actually in my case the label and other controls in a cell are in UIStackView, so to access the label view I use:
let view = cell.contentView.subviews.first?.subviews.first
instead of:
let view = cell.contentView.subviews.first
because in this case my labels are one level further in the cell.

UITableViewCell with a UIView subclass creates multiple layers on cell

I have an custom UITableViewCell created with (.xib) and have UIView subclass instance inside cell created from code
table delegate method :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCellOne", forIndexPath: indexPath) as! CustomOneCell
let woView = ModuleView(frame: CGRect(x: 10, y: 10, width: 400, height: 400))
woView.addBehavior(self.modulesForSubjects[indexPath.row])
// remove subviews
for subview in cell.subviews{
subview.removeFromSuperview();
}
// then add your view
cell.wiView = woView
return cell
}
my full UIView subclass :
class ModuleView: UIView {
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
func addBehavior (ArrayOfmodulesForSubject: [SubjectsModules]) {
var number :CGFloat = 30
var number2 :CGFloat = 30
print(ArrayOfmodulesForSubject.count)
for (i, index) in ArrayOfmodulesForSubject.enumerate() {
let yourLabel1 = UILabel(frame: CGRectMake(number, 40 , 35 , 30))
let yourLabel2 = UILabel(frame: CGRectMake(number2, 60 , 140 , 30))
yourLabel1.textColor = UIColor.whiteColor()
yourLabel1.backgroundColor = UIColor.redColor()
yourLabel1.text = String(index.score)
yourLabel2.textColor = UIColor.whiteColor()
yourLabel2.backgroundColor = UIColor.blackColor()
yourLabel2.text = index.date
self.addSubview(yourLabel1)
self.addSubview(yourLabel2)
number += 30
number2 += 70
}
}
here is what i get after some scrolling:
UPDATE rnsjtngus solution, unfortunately also didn't helped (maybe im doing something wrong)
class CustomOneCell: UITableViewCell {
var arr = [SubjectsModules]()
#IBOutlet weak var wiView: ModuleView!
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?){
// Initialize your custom cell
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.setupWoView(self.arr)
}
override func awakeFromNib() {
print("AWAKED FROM NIB")
for subview in subviews{
subview.removeFromSuperview()
if superview != nil {
superview!.removeFromSuperview()
}
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setupWoView(arrayOfmodulesForSubject: [SubjectsModules]) {
let woView = ModuleView(frame: CGRect(x: 10, y: 10, width: 400, height: 400))
woView.backgroundColor = UIColor.blueColor()
self.addSubview(woView)
}
}
Simple but, bad solution is
just remove subviews from cell before you add your own view
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCellOne", forIndexPath: indexPath) as! CustomOneCell
let woView = ModuleView(frame: CGRect(x: 10, y: 10, width: 400, height: 400))
woView.addBehavior(self.modulesForSubjects[indexPath.row])
// remove subviews
for subview in cell.subviews{
subview.removeFromSuperview();
}
// then add your view
cell.addSubview(woView)
return cell
}
When you call dequeue the method dequeues an existing cell if one is available or creates a new one using the class or nib if not.
This means you may well get one that has already had a subview added to it and when you dequeue it again your code would then add a second one. You need to start by removing any existing subviews.

custom UICollectionViewCell class recognize but not working

I implemented a custom class for my collectionview cells.
The class recognize the attributes like labels but when i run my app the label text doesn't display.
Class is register like this :
self.collectionView!.registerClass(StatsCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
here is the init method of the custom cell class:
var textlabel = UILabel()
override init(frame: CGRect) {
let width = frame.size.width
let height = frame.size.height
textlabel = UILabel(frame: CGRectMake(0, 0, width,height))
super.init(frame: frame)
}
and the implementation :
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StatsCollectionViewCell
// Configure the cell
cell.textlabel.text = "test"
return cell
}
Could someone point me to my mistake please, i'm a bit lost here, cannot see what's wrong.
Note that i can use the cell properties (meaning the cell is in fact here).
You didn't put textlabel in a view.
override init(frame: CGRect) {
let width = frame.size.width
let height = frame.size.height
textlabel = UILabel(frame: CGRectMake(0, 0, width,height))
super.init(frame: frame)
addSubview(textlabel) // add text label to view
}