I use UICollectionView and refer to this link implement scroll table view Fixed single column and row like below image.
but when I want to add shadow property, it display messy.
I think the reason is that I reuse cell when render visual view, but I don't know how to fix it :(
provide my code below, thanks for your time!
import Foundation
import SwiftyJSON
#objc(RNCollection)
class RNCollection : UICollectionView, UICollectionViewDataSource, UICollectionViewDelegate {
var contentCellIdentifier = "CellIdentifier"
var collectionView: UICollectionView!
static var dataSource = []
static var sections = 0
static var rows = 0
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: CGRectZero, collectionViewLayout: RNCollectionLayout())
self.registerClass(RNCollectionCell.self, forCellWithReuseIdentifier: contentCellIdentifier)
self.directionalLockEnabled = false
self.backgroundColor = UIColor.whiteColor()
self.delegate = self
self.dataSource = self
self.frame = frame
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setConfig(config: String!) {
var json: JSON = nil;
if let data = config.dataUsingEncoding(NSUTF8StringEncoding) {
json = JSON(data: data);
};
RNCollection.dataSource = json["dataSource"].arrayObject!
RNCollection.sections = json["sections"].intValue
RNCollection.rows = json["rows"].intValue
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let contentCell: RNCollectionCell = collectionView.dequeueReusableCellWithReuseIdentifier(contentCellIdentifier, forIndexPath: indexPath) as! RNCollectionCell
contentCell.textLabel.textColor = UIColor.blackColor()
if indexPath.section == 0 {
contentCell.backgroundColor = UIColor(red: 232/255.0, green: 232/255.0, blue: 232/255.0, alpha: 1.0)
if indexPath.row == 0 {
contentCell.textLabel.font = UIFont.systemFontOfSize(16)
contentCell.textLabel.text = (RNCollection.dataSource[0] as! NSArray)[0] as? String
} else {
contentCell.textLabel.font = UIFont.systemFontOfSize(14)
contentCell.textLabel.text = (RNCollection.dataSource[0] as! NSArray)[indexPath.row] as? String
}
} else {
contentCell.textLabel.font = UIFont.systemFontOfSize(12)
contentCell.backgroundColor = UIColor.whiteColor()
if(indexPath.section % 2 == 0) {
contentCell.backgroundColor = UIColor(red: 234/255.0, green: 234/255.0, blue: 236/255.0, alpha: 1.0)
}
if indexPath.row == 0 {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
}
}
return contentCell
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return RNCollection.rows
}
// MARK - UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return RNCollection.sections
}
}
You are specifying your customizations for a certain section/row and in your else condition you just set the text. You should specify your required/default customization in your else condition. Consider this part of your code
if indexPath.row == 0 {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
}
You have specified your shadowOffset,shadowOpacity and shadowRadius in your if condition, but you ignored them in your else condition. If you specify the appearance in the else condition you won't have the problem you are facing now. It should look something like this
if indexPath.row == 0 {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
contentCell.layer.shadowOffset = 0
contentCell.layer.shadowOpacity = 0
contentCell.layer.shadowRadius = 0
}
If you reset everything properly in all your condition checks, this problem won't occur.
You set up shadow when your row is equal to 0:
if indexPath.row == 0 {...}
but if the row is not equal to zero collection view can reuse the cell when the shadow was already set. The solution is reset the shadow when row is not 0, in your code you can do something like this (see todo):
if indexPath.row == 0 { // Set up shadow here
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[0] as? String
contentCell.layer.shadowOffset = CGSize(width: 3, height: 3)
contentCell.layer.shadowOpacity = 0.7
contentCell.layer.shadowRadius = 2
} else {
contentCell.textLabel.text = (RNCollection.dataSource[indexPath.section] as! NSArray)[indexPath.row] as? String
//TODO: Remove the shadow here
}
If you wanna save the cell setting everytime when your cell appears,you should set every possible condition of the method "func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell".Do not prevent the reuse of collectionView only if your data or items is few.
In Objective-C, but in Swift, the logic should be the same and not that hard to translate.
In RNCollectionCell.
//These enums may have a better nam, I agree
typedef enum : NSUInteger {
RNCollectionCellStyleColumnTitle,
RNCollectionCellStyleColumnValue,
RNCollectionCellStyleRowTitle,
RNCollectionCellStyleUpperLeftCorner,
} RNCollectionCellStyle;
-(void)titleIt:(NSString *)title forStyle:(RNCollectionCellStyle)style forSection:(NSUInteger)section
{
self.textLabel.textColor = [UIColor blackColor]; //Since it's always the same could be done elsewhere once for all.
//Note I didn't check completely if the case were good, and I didn't add the background color logic, but it should be done here too.
switch (style)
{
case RNCollectionCellStyleColumnTitle:
{
self.textLabel.font = [UIFont systemFontOfSize:12];
self.layer.shadowOffset = CGSizeMake(3, 3);
self.layer.shadowOpacity = 0.7;
self.layer.shadowRadius = 2;
}
break;
case RNCollectionCellStyleColumnValue:
{
self.textLabel.font = [UIFont systemFontOfSize:12];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
break;
case RNCollectionCellStyleRowTitle:
{
self.textLabel.font = [UIFont systemFontOfSize:14];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
break;
case RNCollectionCellStyleUpperLeftCorner:
{
self.textLabel.font = [UIFont systemFontOfSize:16];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
break;
default:
break;
}
self.textLabel.text = title;
}
-(void)prepareForReuse
{
[super prepareForReuse];
self.layer.shadowOffset = CGSizeZero;
self.layer.shadowOpacity = 0;
self.layer.shadowRadius = 0;
}
In RNCollection:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
{
RNCollectionCell *contentCell = [collectionView dequeueReusableCellWithReuseIdentifier: contentCellIdentifier forIndexPath:indexPath];
//Here check what style you need to do
//Also, you seems to always do dataSource[indexPath.section][indexPath.row], you just use the "if test values".
[contentCell titleIt:dataSource[indexPath.section][indexPath.row]
forStyle:RNCollectionCellStyleColumnTitle
forSection:[indexPath section]];
return contentCell;
}
As said, I didn't check if all the cases were correct according to your needs.
But, you don't "prevent reuse of cell", it's optimized.
You need to "reset" values before reused or when you set the values. In theory the thing done in prepareForReuse should be enough and you shouldn't have to "reset" the layer effect in the switch, but I honestly didn't check it.
Related
In my app I have to show multiple progress views in a single tableViewCell, the progress as per actual to goal and it showing correctly when cell is loaded for the first time as per image1
but after scrolling the table the cell is visible like image2.
The progress of the progress views is altered after scrolling the tableview. I have printed the progress values in cellForRowAt method and they are correct but the progress in progress view is being altered. I am setting the progressview progress in cellForRowAt method.
Can anybody help me to resolve this issue?
#Bhavin Kansagara here is my code in cellForRowAt method.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cellIdentifier = "myIdentifier"
var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? MyTableViewCell
if cell == nil {
tableView.register(UINib.init(nibName: "MyTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier)
cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? MyTableViewCell
}
let myDict = self.myArray[indexPath.row]
let goalDict = myDict["goal"]!
let actualDict = myDict["actual"]!
for label in cell!.actualValueLabels {
switch label.tag {
case 0:
label.text = String(actualDict["value1"]!)
case 1:
label.text = String(actualDict["value2"]!)
case 2:
label.text = String(actualDict["value3"]!)
case 3:
label.text = String(actualDict["value4"]!)
case 4:
label.text = String(actualDict["value5"]!)
case 5:
label.text = String(actualDict["total"]!)
default:
break
}
}
for label in cell!.goalValueLabels {
switch label.tag {
case 0:
label.text = String(goalDict["value1"]!)
case 1:
label.text = String(goalDict["value2"]!)
case 2:
label.text = String(goalDict["value3"]!)
case 3:
label.text = String(goalDict["value4"]!)
case 4:
label.text = String(goalDict["value5"]!)
case 5:
label.text = String(goalDict["total"]!)
default:
break
}
}
let greenColor = UIColor(red: 146/255, green: 209/255, blue: 78/255, alpha: 1)
let yellowColor = UIColor(red: 1, green: 122/255, blue: 0, alpha: 1)
let value1Goal = goalDict["value1"]!
let value1Actual = actualDict["value1"]!
cell?.value1ProgressView.progressTintColor = value1Actual >= value1Goal ? greenColor : yellowColor
if value1Goal == 0 {
DispatchQueue.main.async {
cell?.value1ProgressView.setProgress(Float(value1Actual), animated: true)
}
} else {
DispatchQueue.main.async {
cell?.value1ProgressView.setProgress(Float(value1Actual)/Float(value1Goal), animated: true)
}
}
let value2Goal = goalDict["value2"]!
let value2Actual = actualDict["value2"]!
cell?.value2ProgressView.progressTintColor = value2Actual >= value2Goal ? greenColor : yellowColor
if value2Goal == 0 {
DispatchQueue.main.async {
cell?.value2ProgressView.setProgress(Float(value2Actual), animated: true)
}
} else {
DispatchQueue.main.async {
cell?.value2ProgressView.setProgress(Float(value2Actual)/Float(value2Goal), animated: true)
}
}
let value3Goal = goalDict["value3"]!
let value3Actual = actualDict["value3"]!
cell?.value3ProgressView.progressTintColor = value3Actual >= value3Goal ? greenColor : yellowColor
if value3Goal == 0 {
DispatchQueue.main.async {
cell?.value3ProgressView.setProgress(Float(value3Actual), animated: true)
}
} else {
DispatchQueue.main.async {
cell?.value3ProgressView.setProgress(Float(value3Actual)/Float(value3Goal), animated: true)
}
}
let value4Goal = goalDict["value4"]!
let value4Actual = actualDict["value4"]!
cell?.value4ProgressView.progressTintColor = value4Actual >= value4Goal ? greenColor : yellowColor
if value4Goal == 0 {
DispatchQueue.main.async {
cell?.value4ProgressView.setProgress(Float(value4Actual), animated: true)
}
} else {
DispatchQueue.main.async {
cell?.value4ProgressView.setProgress(Float(value4Actual)/Float(value4Goal), animated: true)
}
}
let value5Goal = goalDict["value5"]!
let value5Actual = actualDict["value5"]!
cell?.value5ProgressView.progressTintColor = value5Actual >= value5Goal ? greenColor : yellowColor
if value5Goal == 0 {
DispatchQueue.main.async {
cell?.value5ProgressView.setProgress(Float(value5Actual), animated: true)
}
} else {
DispatchQueue.main.async {
cell?.value5ProgressView.setProgress(Float(value5Actual)/Float(value5Goal), animated: true)
}
}
let totalGoal = goalDict["total"] as! Int
let totalActual = actualDict["count"]!
cell?.totalProgressView.progressTintColor = totalActual >= totalGoal ? greenColor : yellowColor
if totalGoal == 0 {
DispatchQueue.main.async {
cell?.totalProgressView.setProgress(Float(totalActual), animated: true)
}
} else {
DispatchQueue.main.async {
cell?.totalProgressView.setProgress(Float(totalActual)/Float(totalGoal), animated: true)
}
}
return cell!
}
else {
//another cells are used in other sections
}
}
here myArray = [[String: [String: Int]]]
Thanks in advance.
There is one way to handle this situations, You should try setting the different identifier for the cell's but that will affect your performance. after setting this approach, you should check for the performance of scrolling on your device.
let cellIdentifier = "myIdentifier_\(indexPath.section)_\(indexPath.row)"
var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? MyTableViewCell
if cell == nil {
tableView.register(UINib.init(nibName: "MyTableViewCell", bundle: nil), forCellReuseIdentifier: cellIdentifier)
cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? MyTableViewCell
}
This is not the best approach, but can help avoid the repeating process issue.
Try and share results.
I've been having this problem for weeks and I think I am not able to solve it.
I have a tableView which has custom tableViewCells and they are filled with data. The tableViewCell has two UILabels and one UIView.
The problem appears when I scroll several times the drawings on the UIViews are overlapped one with another, I think they are redrawn. I know that this behavior is because of the reuse of the cells but I can't even locate the origin of the problem.
UIViews show perfectly when opening the app
UIViews get overlapped after scrolling
My UITableView's cellForRowAtIndexPath is:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellIdentifier = "Cell"
let cell: CustomTableViewcell = self.tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath) as! CustomTableViewcell
//self.tableView.dequeueReusableCellWithIdentifier(CellIdentifier) as! CustomTableViewcell
cell.prepareForReuse()
self.createUIForCell(cell)
self.configureCell(cell, indexPath: indexPath)
print("\t\(parsedData[indexPath.row].stationName): \(parsedData[indexPath.row].freeBikes)")
return cell
}
func createUIForCell(cell: CustomTableViewcell) {
cell.distanceLabel.textColor = UIColor.whiteColor()
cell.distanceLabel.backgroundColor = UIColor.clearColor()
cell.bikeStationLabel.textColor = UIColor.whiteColor()
cell.bikeStationLabel.backgroundColor = UIColor.clearColor()
}
func configureCell(cell: CustomTableViewcell, indexPath: NSIndexPath) {
let stations = parsedData[indexPath.row]
if indexPath.row % 2 == 0 {
cell.stackView.arrangedSubviews.first!.backgroundColor = cellBackgroundColor1
cell.progressView.backgroundColor = cellBackgroundColor2
} else {
cell.stackView.arrangedSubviews.first!.backgroundColor = cellBackgroundColor2
cell.progressView.backgroundColor = cellBackgroundColor1
}
cell.progressView.getWidth(stations.freeBikes, freeDocks: stations.freeDocks)
cell.getLocation(stations.latitude, longitude: stations.longitude)
cell.getParameters(stations.freeBikes, freeDocks: stations.freeDocks)
cell.bikeStationLabel.text = stations.stationName
if stations.distanceToLocation != nil {
if stations.distanceToLocation! < 1000 {
cell.distanceLabel.text = String(format: "%.f m", stations.distanceToLocation!)
} else {
cell.distanceLabel.text = String(format: "%.1f km", stations.distanceToLocation! / 1000)
}
} else {
cell.distanceLabel.text = ""
}
}
For the before mentioned UIVIew inside my custom cell I have created a separated class for it to handle the drawing and it looks like this:
import UIKit
class ProgressViewBar: UIView {
var freeBikes = 0
var freeDocks = 0
var text: UILabel? = nil
var text2: UILabel? = nil
func getWidth(freeBikes: Int, freeDocks: Int) {
self.freeBikes = freeBikes
self.freeDocks = freeDocks
}
override func drawRect(rect: CGRect) {
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: rect.width * (CGFloat(freeBikes) / (CGFloat(freeBikes) + CGFloat(freeDocks))), height: frame.height), cornerRadius: 0)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.CGPath
shapeLayer.strokeColor = UIColor.whiteColor().CGColor
shapeLayer.fillColor = UIColor.whiteColor().CGColor
self.layer.addSublayer(shapeLayer)
drawText(rect)
}
func drawText(rect: CGRect) {
if freeBikes != 0 {
text?.removeFromSuperview()
text = UILabel(frame: CGRect(x: 2, y: 0, width: 20, height: 20))
text!.text = String("\(freeBikes)")
text!.textAlignment = NSTextAlignment.Left
text!.font = UIFont(name: "HelveticaNeue-Thin", size: 17)
text!.backgroundColor = UIColor.clearColor()
text!.textColor = UIColor(red: 1/255, green: 87/255, blue: 155/255, alpha: 1)
self.addSubview(text!)
}
text2?.removeFromSuperview()
text2 = UILabel(frame: CGRect(x: rect.width * (CGFloat(freeBikes) / (CGFloat(freeBikes) + CGFloat(freeDocks))) + 2, y: 0, width: 20, height: 20))
text2!.text = String("\(freeDocks)")
text2!.textAlignment = NSTextAlignment.Left
text2!.font = UIFont(name: "HelveticaNeue-Thin", size: 17)
text2!.backgroundColor = UIColor.clearColor()
text2!.textColor = UIColor.whiteColor()
self.addSubview(text2!)
}
}
The view needs two parameters to be drawn and I pass them using the func getWidth(freeBikes: Int, freeDocks: Int) method calling it from the `ViewController. If any other piece of code is needed you can look at the repo.
The problem is that when reusing the cell you call drawRect once again and don't clear the already drawn rect. Clear it before drawing another one.
I'm trying to use UICollectionView to bind different cells with an XIB file and set a design for it.
I know how to bind different cells and it's works fine in my application.
Here the code to bind:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell : UICollectionViewCell
mCurrentIndexPath = indexPath
// HEADER
switch indexPath.section {
case 0:
cell = configureModuleHeaderCell(indexPath)
default:
// Local
let theme = getThemeFromIndex(indexPath.section - 1)
mCurrentDocuments = getDocumentsFromTheme(theme)
let cours : DownloadableDocument? = (mCurrentDocuments != nil) ? getCoursForTheme() : nil
mCurrentDocuments = deleteCoursFromDocAnnexe()
mCurrentDocuments = sortDocumentDoublePDF()
if indexPath.row == 0 {
cell = configureThemeHeaderCell(theme, cours: cours)
}
// NORMAL
else {
cell = configureThemeDocCell()
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.grayColor().CGColor
}
break
}
return cell
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
if mFetchedResultController != nil {
mCurrentIndexPath = NSIndexPath(forRow: 0, inSection: 0)
return mFetchedResultController!.fetchedObjects!.count + 1
}
return 0
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
var counter : Int = 1
switch section {
case 0:
counter = 1
break
default:
mCurrentDocuments = getDocumentsFromTheme(getThemeFromIndex(section - 1))
mCurrentDocuments = checkDoubleCours()
counter = (mCurrentDocuments != nil) ? mCurrentDocuments!.count : 0
counter += (!documentsContainsCours(mCurrentDocuments)) ? 1 : 0
break
}
return counter
}
Then I want to set a border for each section. Is that possible ?
I can set a border for cell by :
cell.layer.borderWidth = 1.0
cell.layer.borderColor = UIColor.grayColor().CGColor
But I want to do it for a section.
I've finally found a solution !
I just need to calculate the height of all cells of a section, set this height on the view and position it well.
EDIT:
Update for #Urmi
Add this function to instanciate a cardView with frame
func instanciateCardView(frame : CGRect) -> UIView {
// Declaration
let view = UIView(frame : frame)
// Basic UI
view.backgroundColor = UIColor.whiteColor()
view.layer.backgroundColor = UIColor.whiteColor().CGColor
view.layer.borderWidth = 1.0
view.layer.borderColor = ColorsUtil.UIColorFromRGB(0xAB9595).CGColor
// Shadow (cardview style)
view.layer.shadowPath = UIBezierPath(rect: view.bounds).CGPath
view.layer.shouldRasterize = true
view.layer.shadowColor = ColorsUtil.UIColorFromRGB(0xD5C6C6).CGColor
view.layer.shadowOpacity = 1
view.layer.shadowOffset = CGSizeMake(1, 1);
view.layer.shadowRadius = 1.0
if !mCardViewList.contains(view) {
// Add view to container - Just to save the instance
mCardViewList.append(view)
// Add view to main view
self.collectionView?.addSubview(view)
self.collectionView?.sendSubviewToBack(view)
}
return view
}
And in the cellForItemAtIndexPath :
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell : UICollectionViewCell
mCurrentIndexPath = indexPath
switch indexPath.section {
case 0:
cell = configureModuleHeaderCell(indexPath)
// Basic UI
cell.backgroundColor = UIColor.whiteColor()
cell.layer.borderWidth = 1.0
cell.layer.borderColor = ColorsUtil.UIColorFromRGB(0xAB9595).CGColor
// Shadow (cardview style)
cell.layer.shadowPath = UIBezierPath(rect: view.bounds).CGPath
cell.layer.shouldRasterize = true
cell.layer.shadowColor = ColorsUtil.UIColorFromRGB(0xD5C6C6).CGColor
cell.layer.shadowOpacity = 1
cell.layer.shadowOffset = CGSizeMake(1, 1);
cell.layer.shadowRadius = 1.0
break
default:
let theme = getThemeFromIndex(indexPath.section - 1)
mCurrentDocuments = getDocumentsFromTheme(theme)
let cours : DownloadableDocument? = (mCurrentDocuments != nil) ? getCoursForTheme() : nil
mCurrentDocuments = deleteCoursFromDocAnnexe()
mCurrentDocuments = sortDocumentDoublePDF(mCurrentDocuments)
if indexPath.row == 0 {
cell = configureThemeHeaderCell(theme, cours: cours)
if mCurrentDocuments != nil {
if mCurrentDocuments!.count == 0 {
instanciateCardView(CGRectMake(cell.frame.origin.x - 30, cell.frame.origin.y - 10, cell.frame.width + 60, cell.frame.height + 20))
}
}
}
else {
cell = configureThemeDocCell()
if indexPath.row == mCollectionView.numberOfItemsInSection(indexPath.section) - 1 {
// Attribute
let index = NSIndexPath(forRow: 0, inSection: indexPath.section)
let headerAttribute : UICollectionViewLayoutAttributes = mCollectionView.layoutAttributesForItemAtIndexPath(index)!
let height = (cell.frame.origin.y + cell.frame.height) - (headerAttribute.frame.origin.y - 10)
instanciateCardView(CGRectMake(headerAttribute.frame.origin.x - 30, headerAttribute.frame.origin.y - 10, headerAttribute.frame.width + 60, height + 20))
}
}
break
}
return cell
}
I've found that is not the best solution, but it works. Now i've declare a UITableView and add a UICollectionView in each UITableViewCell
Don't forget to upvote if it helps you :)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! StackingCell
let data = stack[indexPath.row]
cell.stackDeatils = data
cell.floorNumber_Label.text = data.floorNo
let floorDetails = data.floorDetails!
if floorDetails == "NA"{
cell.floor_View.backgroundColor = UIColor.lightGrayColor()
}
else {
for items in floorDetails
{
let floorArr = items.1
cell.floor_View.backgroundColor = UIColor.orangeColor()
//Number of Units passing value to cell
cell.secitons = floorArr.count
for units in floorArr {
let unit = units.1
let compName = unit["companyName"].string!
if compName == "Vacant" {
cell.floor_View.backgroundColor = UIColor(red: 94/255, green: 198/255, blue: 162/255, alpha: 1.0)
}
}
}
}
}
let shadowPath = UIBezierPath(rect: cell.floor_View.bounds)
cell.floor_View.layer.masksToBounds = false
cell.floor_View.layer.shadowColor = UIColor.blackColor().CGColor
cell.floor_View.layer.shadowOffset = CGSize(width: 1, height: 0.5)
cell.floor_View.layer.shadowOpacity = 0.2
cell.floor_View.layer.shadowPath = shadowPath.CGPath
return cell
}
CustomTableCell method for adding vertical line to TableViewCell
var stackDeatils:StackingModel!{
didSet
{
dispatch_async(dispatch_get_main_queue()) {
self.updateData()
}
}
}
func updateData(){
let unit = self.stackDeatils.isUnitsDrawn
if unit == true {
self.stackDeatils.isUnitsDrawn = false
let stack_width = self.floor_View.frame.width
let stack_height = self.floor_View.frame.height
xCor = stack_width/CGFloat(self.secitons)
var x = xCor
for var i = 1;i < self.secitons; i++ {
let line = UIView(frame: CGRectMake(x, 0, 2,stack_height))
line.backgroundColor = UIColor.whiteColor()
//print(xCor)
self.floor_View.addSubview(line)
x += xCor
}
}
else {
print(self.stackDeatils.isUnitsDrawn)
}
}
I have created a class of message including, content and sender. I successfully store the desired data in Parse and I am querying them. So far, no problem. Then, I attempt to filter the messages according to the sender, or the receiver, in order to display them in different manners on my tableView.
For instance, let's say that is sender is currentUser, the text is blue, otherwise it is green.
If currentUser sends a message, all the text becomes blue, even the one sent by the other user; and vice versa.
class Message {
var sender = ""
var message = ""
var time = NSDate()
init(sender: String, message: String, time: NSDate)
{
self.sender = sender
self.message = message
self.time = time
}
}
var message1: Message = Message(sender: "", message: "", time: NSDate())
func styleTextView()
{
if message1.sender == PFUser.currentUser()?.username {
self.textView.textColor = UIColor.blueColor()
} else {
self.textView.textColor = UIColor.greenColor()
}
}
func addMessageToTextView(message1: Message)
{
textView.text = message1.message
self.styleTextView()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell: messageCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! messageCell
let message = self.array[indexPath.row]
cell.setupWithMessage(message)
return cell
}
I have other codes on my viewController, however I believe they are irrelevant to this matter; if needed, I can provide them.
Any idea why I cannot have the textViews in different style according to the sender?
They are all either blue, either green.
Below is the code for the full set up of the tableViewCell:
class messageCell: UITableViewCell {
private let padding: CGFloat = 10.0
var array1 = [Message]()
private func styleTextView()
{
let halfTextViewWidth = CGRectGetWidth(textView.bounds) / 2.0
let targetX = halfTextViewWidth + padding
let halfTextViewHeight = CGRectGetHeight(textView.bounds) / 2.0
self.textView.font = UIFont.systemFontOfSize(12.0)
if PFUser.currentUser()?.username == message1.sender && textView.text == message1.message {
self.textView.backgroundColor = UIColor.blueColor()
self.textView.layer.borderColor = UIColor.blueColor().CGColor
self.textView.textColor = UIColor.whiteColor()
textView.center.x = targetX
textView.center.y = halfTextViewHeight
} else {
self.textView.backgroundColor = UIColor.orangeColor()
self.textView.layer.borderColor = UIColor.orangeColor().CGColor
self.textView.textColor = UIColor.whiteColor()
self.textView.center.x = CGRectGetWidth(self.bounds) - targetX
self.textView.center.y = halfTextViewHeight
}
}
private lazy var textView: MessageBubbleTextView = {
let textView = MessageBubbleTextView(frame: CGRectZero, textContainer: nil)
self.contentView.addSubview(textView)
return textView
}()
class MessageBubbleTextView: UITextView
{
override init(frame: CGRect = CGRectZero, textContainer: NSTextContainer? = nil)
{
super.init(frame: frame, textContainer: textContainer)
self.scrollEnabled = false
self.editable = false
self.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)
self.layer.cornerRadius = 15
self.layer.borderWidth = 2.0
}
required init(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
}
private let minimumHeight: CGFloat = 30.0
private var size = CGSizeZero
private var maxSize: CGSize {
get {
let maxWidth = CGRectGetWidth(self.bounds)
let maxHeight = CGFloat.max
return CGSize(width: maxWidth, height: maxHeight)
}
}
func setupWithMessage(message: Message) -> CGSize
{
textView.text = message.message
size = textView.sizeThatFits(maxSize)
if size.height < minimumHeight {
size.height = minimumHeight
}
textView.bounds.size = size
self.styleTextView()
return size
}
}
One possible error can be in this line:
if message1.sender == PFUser.currentUser()?.username
where you are comparing different types since message1 is a String whereas PFUser.currentUser()?.username is an optional String. The right way to compare them is:
if let username = PFUser.currentUser()?.username where username == message1.sender {
self.textView.textColor = UIColor.blueColor()
} else {
self.textView.textColor = UIColor.greenColor()
}
This is because you are changing the text color of the entire UITextView with your code. See the following post for a start in the right direction: Is it possible to change the color of a single word in UITextView and UITextField