there is a weird issue about UITableView. I have 3 data source it means 3 section in UITableView. When I scroll the UITableView, button and images are conflicting. Button is disappearing, images are becoming deformed.
Here my cellForRowAt method.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventnetworkingcell", for: indexPath) as! EventNetworkCell
var name: String = ""
var job: String = ""
var company: String = ""
var img: Int?
cell.userImageView.layer.cornerRadius = 45
cell.userImageView.clipsToBounds = true
if indexPath.section == 0 {
name = self.matchmaking[indexPath.row].name
job = self.matchmaking[indexPath.row].job
company = self.matchmaking[indexPath.row].company
img = self.matchmaking[indexPath.row].image
}
else if indexPath.section == 1 {
name = self.networkInEvent[indexPath.row].name
job = self.networkInEvent[indexPath.row].job
company = self.networkInEvent[indexPath.row].company
img = self.networkInEvent[indexPath.row].image
cell.addButtonOutlet.alpha = 0
}
else {
name = self.allAttendees[indexPath.row].name
job = self.allAttendees[indexPath.row].job
company = self.allAttendees[indexPath.row].company
img = self.allAttendees[indexPath.row].image
}
cell.nameLabel.text = name
cell.jobLabel.text = job
cell.companyLabel.text = company
if let imgid = img {
let url = MTApi.url(for: imgid, size: .normal)
cell.userImageView.sd_setImage(with: url, placeholderImage: nil, options: [], completed: nil)
}
}
cell.addButtonOutlet.addTarget(self, action:
#selector(self.addNetwork(sender:)), for: .touchUpInside)
return cell
}
When I remove the cell.addButtonOutlet.alpha = 0 line, buttons aren't disappearing.
And there is a video which shows the issue:
Video
You're having an issue with cells reusing.
Basically, what's happening, is that the tableView creates only enough cells as much as there are visible on the screen, and once a cell gets scrolled out of view, it gets reused for another item in your dataSource.
The reason for buttons seemingly disappearing is that that you've already removed the button previously, but now, when reusing haven't told the cell to show the button again.
Fixing this is easy, just add:
cell.addButtonOutlet.alpha = 0
to your sections 0 and 2 (else block).
Same thing with images, the previous image is retained unless you tell the cell to remove the image if needed, so just add this:
if let imgid = img {
let url = MTApi.url(for: imgid, size: .normal)
cell.userImageView.sd_setImage(with: url, placeholderImage: nil, options: [], completed: nil)
} else {
cell.userImageView.image = nil
}
var name: String = ""
var job: String = ""
var company: String = ""
var img: Int?
cell.userImageView.layer.cornerRadius = 45
cell.userImageView.clipsToBounds = true
cell.addButtonOutlet.alpha = 1 // add more
if indexPath.section == 0 {
........
and you should research about UITableviewCell reuse.
Related
I'm writing a demo to show user's tweets.
The question is:
Every time I scroll to the bottom and then scroll back, the tweet's images and comments are reloaded, even the style became mess up. I know it something do with dequeue, I set Images(which is an array of UIImageView) to [] every time after dequeue, but it is not working. I'm confused and couldn't quite sleep....
Here is core code of my TableCell(property and Images set), which provide layout:
class WechatMomentListCell: UITableViewCell{
static let identifier = "WechatMomentListCell"
var content = UILabel()
var senderAvatar = UIImageView()
var senderNick = UILabel()
var images = [UIImageView()]
var comments = [UILabel()]
override func layoutSubviews() {
//there is part of Image set and comments
if images.count != 0 {
switch images.count{
case 1:
contentView.addSubview(images[0])
images[0].snp.makeConstraints{ (make) in
make.leading.equalTo(senderNick.snp.leading)
make.top.equalTo(content.snp.bottom)
make.width.equalTo(180)
make.height.equalTo(180)
}
default:
for index in 0...images.count-1 {
contentView.addSubview(images[index])
images[index].snp.makeConstraints{ (make) in
make.leading.equalTo(senderNick.snp.leading).inset(((index-1)%3)*109)
make.top.equalTo(content.snp.bottom).offset(((index-1)/3)*109)
make.width.equalTo(90)
make.height.equalTo(90)
}
}
}
}
if comments.count != 0, comments.count != 1 {
for index in 1...comments.count-1 {
comments[index].backgroundColor = UIColor.gray
contentView.addSubview(comments[index])
comments[index].snp.makeConstraints{(make) in
make.leading.equalTo(senderNick)
make.bottom.equalToSuperview().inset(index*20)
make.width.equalTo(318)
make.height.equalTo(20)
}
}
}
}
Here is my ViewController, which provide datasource:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let tweetCell = tableView.dequeueReusableCell(withIdentifier: WechatMomentListCell.identifier, for: indexPath) as? WechatMomentListCell else {
fatalError("there is no WechatMomentList")
}
let tweet = viewModel.tweetList?[indexPath.row]
for i in tweet?.images ?? [] {
let flagImage = UIImageView()
flagImage.sd_setImage(with: URL(string: i.url))
tweetCell.images.append(flagImage)
}
for i in tweet?.comments ?? [] {
let flagComment = UILabel()
flagComment.text = "\(i.sender.nick) : \(i.content)"
tweetCell.comments.append(flagComment)
}
return tweetCell
}
The Images GET request has been define at ViewModel using Alamofire.
The firsttime is correct. However, If I scroll the screen, the comments will load again and images were mess up like this.
I found the problem in your tableview cell. in cell you have two variables like this.
var images = [UIImageView()]
var comments = [UILabel()]
Every time you using this cell images and comments are getting appended. make sure you reset these arrays every time you use this cell. like setting theme empty at initialization.
I am using collection view and we have added bubble to the cell.
Also, bubble has the different fill color and border color.
Please find attachment for details.
but when we scroll collection view that time bubble color sometime changed to different and again restored to correct color.
Here's my code:
func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// INIT CELLS INSIDE COLLECTION VIEW
if(collectionView == customContentCollectionView){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: contentCellId, for: indexPath) as! MyContentCell
setupContentCellComponents(cell: cell)
// Configure the cell
cell.horizontalLine.backgroundColor = Color.lightBlue
var labelColour = UIColor()
// If we found record
if(self.bubbleArray[indexPath.section][indexPath.item].interactions != ""){ // GENERATING CUSTOM BUBBLE COLOR AS PER INTERACTIONS
let (bubbleBorder, bubbleFill, labelColor, inspectorNav) = getBubbleColor(controlType: controlParam, count: Int(self.bubbleArray[indexPath.section][indexPath.item].interactions)!, selected: false)
cell.shapeLayer.strokeColor = bubbleBorder.cgColor
cell.shapeLayer.fillColor = bubbleFill.cgColor
cell.gradient.colors = [bubbleFill.cgColor, bubbleFill.cgColor]
labelColour = labelColor
}
cell.labelCount.font = UIFont(name: cellFontName, size: cellFontSize)
cell.labelCount.text = self.bubbleArray[indexPath.section][indexPath.item].interactions
if (self.bubbleArray[indexPath.section][indexPath.item].umid != ""){
cell.tag = Int(self.bubbleArray[indexPath.section][indexPath.item].umid)!
cell.labelCount.tag = Int(self.bubbleArray[indexPath.section][indexPath.item].umid)!
cell.labelCount.textColor = labelColour
}
else (self.bubbleArray[indexPath.section][indexPath.item].umid == ""){ // REMOVING BUBBLE IF NO CONTENT
cell.shapeLayer.removeFromSuperlayer()//remove from superview
}
}
Here's scrolling logic :
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// selectedSection is the value where you have tapped
if(selectedSection != -1){
for i in 0...numberOfItemsInSection{ // CODE TO MAINTAIN ROW HIGHLIGHT POST SCROLL
let newIndices = IndexPath(row: i, section: selectedSection)
for visibleIndices in customContentCollectionView.indexPathsForVisibleItems{
if(newIndices == visibleIndices){
print("NEW INDICES: \(newIndices)")
if(newIndices.section == selectedSection){
selectedRowCells.append(customContentCollectionView.cellForItem(at: newIndices)!)
}
// horizontal line for selected bubble
let singleCell : MyContentCell = customContentCollectionView.cellForItem(at: newIndices)! as! MyContentCell
singleCell.horizontalLine.backgroundColor = UIColor.white
if(previouslySelectedIndex != nil && visibleIndices == previouslySelectedIndex){
changeBubbleColor(index: previouslySelectedIndex, selected: true)
break
}
}
}
Try this way,
Here if cell satisfies the condition then properties are set but if the condition is not satisfied at that time cell takes previous properties set in the conditon so you also need to set the properties in else condition also
so try by setting properties as suggested with comment in code below
I hope you will get your solution
func collectionView(_ collectionView: UICollectionView,cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// INIT CELLS INSIDE COLLECTION VIEW
if(collectionView == customContentCollectionView){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: contentCellId, for: indexPath) as! MyContentCell
setupContentCellComponents(cell: cell)
// Configure the cell
cell.horizontalLine.backgroundColor = Color.lightBlue
var labelColour = UIColor()
// If we found record
if(self.bubbleArray[indexPath.section][indexPath.item].interactions != "")
{ // GENERATING CUSTOM BUBBLE COLOR AS PER INTERACTIONS
let (bubbleBorder, bubbleFill, labelColor, inspectorNav) = getBubbleColor(controlType: controlParam, count: Int(self.bubbleArray[indexPath.section][indexPath.item].interactions)!, selected: false)
cell.shapeLayer.strokeColor = bubbleBorder.cgColor
cell.shapeLayer.fillColor = bubbleFill.cgColor
cell.gradient.colors = [bubbleFill.cgColor, bubbleFill.cgColor]
labelColour = labelColor
}
else
{
//set default cell.shapeLayer.strokeColor
//set default cell.shapeLayer.fillColor
//set default cell.gradient.colors
//set default labelColour
}
cell.labelCount.font = UIFont(name: cellFontName, size: cellFontSize)
cell.labelCount.text = self.bubbleArray[indexPath.section][indexPath.item].interactions
if (self.bubbleArray[indexPath.section][indexPath.item].umid != "")
{
cell.tag = Int(self.bubbleArray[indexPath.section][indexPath.item].umid)!
cell.labelCount.tag = Int(self.bubbleArray[indexPath.section][indexPath.item].umid)!
cell.labelCount.textColor = labelColour
}
else
{
//Set default cell.tag
//Set default cell.labelCount.tag
//Set default cell.labelCount.textColor
}
}
While reloading UICollectionview, cell which has a chat count on the top of the cell flashes some random numbers before displaying the original count. Have any idea why this happens?? it happends with in a fraction of seconds
Here is the code what i have used
var profile : HomeDTO = appD.homeDTO!.friendsDetailsArray.objectAtIndex(indexPath.row) as! HomeDTO
var cell : HomeCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("HomeCollectionViewCell", forIndexPath: indexPath) as? HomeCollectionViewCell
self.loadImages(profile.profile_Thumb_image, imageView: cell.m_imgProfile, placeHolderImage: ProfilePlaceholderImage!)
var strFirstName = profile.firstNameName
var strSecondName = profile.lastName
cell.m_lblName.text = strFirstName! + " " + strSecondName!
var sharedInterests = String(profile.interestCount ?? 0)
cell.m_btnSharedInterests.clipsToBounds = true
cell.m_btnSharedInterests.layer.cornerRadius = cell.m_btnSharedInterests.frame.size.width/2
cell.m_btnSharedInterests.userInteractionEnabled = false
cell.m_btnSharedInterests.layer.borderWidth = 3.0
cell.m_btnSharedInterests.backgroundColor = UIColor.whiteColor()
self.updateStatusColor(profile.isOnline!, cell: cell)
cell.m_btnSharedInterests.setTitle(sharedInterests, forState: UIControlState.Normal)
return cell
Found myself what the issue was. I was using UIButton to show the count as it has some related actions following. I replaced it with a label & everything seems to be fixed.
Allright, I think I've read all the issues which seemed to be the same as I did.
I have a UITextView in a UITableViewCell, the cell itself contains an imageView, a UILabel as title, the UITextView and another UILabel.
Screenshot:
I believe the constraints to be correct. Currently the view does an estimate which is correct in most cases, but in my case the content is larger than the UITableViewCell's height, this is probably due to the delayed rendering of the UITableViewCell, causing other list items to draw behind this one (it only appears on top).
The code which estimates the height:
func heightForCellAtIndexPath(indexPath:NSIndexPath) -> CGFloat {
if (calculatorCell[cellIdentifier] == nil) {
calculatorCell[cellIdentifier] = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell
}
if let cell = calculatorCell[cellIdentifier] {
populateCell(cell, indexPath: indexPath)
cell.setNeedsLayout()
cell.layoutIfNeeded()
return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
}
ErrorClass.log("Could not instantiate cell? returning 100 float value instead for listView")
return 100.0
}
This is a generic function as you see since it's handling a UITableViewCell not a custom, so the populateCell in this case, the first index returns the cell with the issue, the rest are comments which are working fine, I'm just sharing it all since people will ask for it:
if (indexPath.row == 0) {
let cellUnwrapped = cell as! ChallengeHeader
cellUnwrapped.setContent(
challenge!.content,
date: StrFormat.deadLineFromDate(challenge!.endAt),
likes: Language.countValue("challenge_count_like", count: challenge!.totalLikes),
likeController: self,
commentCount: Language.countValue("global_count_comment", count: challenge!.totalComments)
)
cellUnwrapped.like(userLiked)
cellUnwrapped.loadAttachments(challenge!.attachments)
return cellUnwrapped
} else {
let cellUnwrapped = cell as! ListItemComment
let challengeComment = items[(indexPath.row - 1)] as! ChallengeComment
var username = "Anonymous"
if let user = challengeComment.author as User? {
cellUnwrapped.iconLoad(user.avatar)
username = user.displayName
}
cellUnwrapped.setContentValues(
StrFormat.cleanHtml(challengeComment.text),
date: StrFormat.dateToShort(challengeComment.createdAt),
username: username
)
return cellUnwrapped
}
The cell's setContentvalues which parses the html string to NSAttributedString, it's wrapped in an if to prevent reloads to re-render the NSAttributedString. It won't change but it does take a while to render:
func setContent(content:String, date:String, likes:String, likeController : LikeController, commentCount:String) {
if (content != txtContent) {
self.contentLabel.text = nil
self.contentLabel.font = nil
self.contentLabel.textColor = nil
self.contentLabel.textAlignment = NSTextAlignment.Left
contentLabel.attributedText = StrFormat.fromHtml(content)
txtContent = content
}
deadlineLabel.text = date
likesLabel.text = likes
self.likeController = likeController
likeButton.addTarget(self, action: "likeClicked:", forControlEvents: .TouchUpInside)
totalComments.text = commentCount
totalComments.textColor = StyleClass.getColor("primaryColor")
doLikeLayout()
}
So TL;DR; I have a UITextView inside a UITableViewCell scaling sizes using autolayout and containing an NSAttributedString to parse html and html links. How come it does not render well, and how to fix it?
I am having a problem creating UILabels to insert into my tableview cells. They insert fine and display fine, the problem comes in when I go to scroll. The ordering of the row's goes haywire and looses it's initial order. For instance, at the beginning, the idLabel's cell text, goes from 0 > 14, in sequential order. After scrolling down and back up, the order could be anything, 5, 0, 10, 3 etc.
Any ideas on why this is happening? I am presuming my attempts to update the label's after cell creation is wrong.
var idArr: NSMutableArray!
// In init function
self.idArr = NSMutableArray()
for ind in 0...14 {
self.idArr[ind] = ind
}
// Create the tableview cells
internal func tableView( tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath ) -> UITableViewCell {
if cell == nil {
let id = self.idArr[indexPath.row] as Int
cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell" )
// Id
let idLabel: UILabel = UILabel( frame: CGRectZero )
idLabel.text = String( id )
idLabel.textColor = UIColor.blackColor()
idLabel.tag = indexPath.row
idLabel.sizeToFit()
idLabel.layer.anchorPoint = CGPointMake( 0, 0 )
idLabel.layer.position.x = 15
idLabel.layer.position.y = 10
idLabel.textAlignment = NSTextAlignment.Center
cell?.addSubview( idLabel )
}
// Update any labels text string
for obj: AnyObject in cell!.subviews {
if var view = obj as? UILabel {
if view.isKindOfClass( UILabel ) {
if view.tag == indexPath.row {
view.text = String( id )
}
}
}
}
// Return the cell
return cell!
}
Thanks for any assistance, I can't see the wood for the trees anymore.
The logic in your cell updating looks off. You are checking that a tag you set at initialization is equal to the index path's row but because the cells should be reused this will not always be the case. If you just remove that tag checking line in the for loop it should work fine.
Also, I'm not sure why you are checking ifKindOfClass after you have optionally cast the view as a label anyway.
To prevent accidentally updating Apple's views you would be better off adding a constant tag for the label and pulling the label by that tag instead of looping through all of the subviews.
This is clearly not the actual code you are running since the id variable is not in the proper scope and there is no definition of cell. I've added proper dequeueing and move the id variable to something that works. I'm assuming you are doing those in your actual code.
internal func tableView( tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath ) -> UITableViewCell {
let labelTag = 2500
let id = self.idArr[indexPath.row] as Int
var cell = tableView.dequeueReusableCellWithIdentifier("cell")
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell" )
// Id
let idLabel: UILabel = UILabel( frame: CGRectZero )
idLabel.text = String( id )
idLabel.textColor = UIColor.blackColor()
idLabel.tag = labelTag
idLabel.sizeToFit()
idLabel.layer.anchorPoint = CGPointMake( 0, 0 )
idLabel.layer.position.x = 15
idLabel.layer.position.y = 10
idLabel.textAlignment = NSTextAlignment.Center
cell?.addSubview( idLabel )
}
// Update any labels text string
if let view = cell?.viewWithTag(labelTag) as? UILabel {
view.text = String( id )
}
// Return the cell
return cell!
}