Text Overlapping Itself in Table Cells Swift

Text overlapping itself in table cells SWIFT

Everytime you update the tableview, it checks the queue to see if it can reuse a cell instead of initializing a new one. In this case, when it updates, it has cells in the queue so you're adding a new label subview everytime the table updates which is causing this effect. In this case, you should only add the label subview if it doesn't exist already. Otherwise, just update the text of that subview.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCellWithIdentifier("Cell",forIndexPath: indexPath) as! UITableViewCell

if let nameLabel = cell.viewWithTag(100) as? UILabel{

var userAtIndexPath = finalMatchesBlurUser[indexPath.row]

nameLabel.text = userAtIndexPath.username.uppercaseString
}
else{
nameLabel = UILabel(frame: CGRectMake(cell.frame.size.width * 0.040, cell.frame.size.height * 0.22, cell.frame.size.width * 0.735, cell.frame.size.height * 0.312))

nameLabel.tag = 100;

var userAtIndexPath = finalMatchesBlurUser[indexPath.row]

nameLabel.text = userAtIndexPath.username.uppercaseString

cell.addSubview(nameLabel)
}
return cell;
}

TableView Cell Custom type Text Overlaps itself or is Blank

***Update - When using custom cells remove the prototype cell as you don't need it!!

I finally figured out why it was not working. The whole problem is the Prototype Cell because you have to set a reuse Identifier and in most of the tutorials they use the string "Cell".

When the tableview load it already has a cell in it with the Identifier "Cell" so it doesn't create a new cell it just reuses the cell that has the Identifier "Cell".

static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

The above block if (cell == nil) is never called because it is reusing the Prototype cell that is already there. The other problem is creating the label below this block as in:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
FailedBankInfo *info = [_fetchedResultsController objectAtIndex:indexPath.row];

CGRect infoFrame = CGRectMake(10, 10, 200, 16);
UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
infoLabel.textColor = [UIColor blackColor];
infoLabel.backgroundColor = [UIColor yellowColor];
infoLabel.font = [UIFont systemFontOfSize:12];
infoLabel.textAlignment = NSTextAlignmentCenter;
infoLabel.tag = 101;
infoLable.text = [NSString stringWithFormat:@"%@, %@",
info.city, info.state];
[cell.contentView addSubview:infoLabel];

return cell;
}

This appears to work fine as it does create the Label but when you scroll the screen you find that Labels are overlapping themselves as in my problem. This is because when a cell goes off screen and comes back on the cellForRowAtIndexPath reuses a cell that already has a label and creates another Label over the top. This keeps happening until you have 7, 8 or more Labels overlapping on the same cell. I worked this out by putting an NSlog in the didSelectRowatIndex to count the number of subviews here:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

NSLog(@"Count is:%i", [[tableView cellForRowAtIndexPath:indexPath].contentView.subviews count]);
}

So what you need to do is create the custom Label inside the if (cell == nil) and use an else to only set the text of the Label. The key for this to work is that each cell has to have a unique reuse Identifier so when CellforRowatIndexPath is called it creates a new cell the first time it appears. When that cell goes off screen and comes back on a new cell is not created it just reuses the existing one and changes the text without creating another Label. I have checked this with another NSlog in the if (cell == nil) it fires once for each cell and after that it doesn't fire because it reuses dequeued cells. I just used a string "myCell" + indexPath.row for the Identifier. In the storyboard I still have the Identifier of "Cell" for the Prototype cell. It complains if you don't have something in there. Here is my final code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *myCellID = [NSString stringWithFormat:@"myCell%i", indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:myCellID];

FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];

if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:myCellID];
CGRect infoFrame = CGRectMake(10, 10, 200, 16);
UILabel *infoLabel = [[UILabel alloc] initWithFrame:infoFrame];
infoLabel.textColor = [UIColor blackColor];
infoLabel.backgroundColor = [UIColor yellowColor];
infoLabel.font = [UIFont systemFontOfSize:12];
infoLabel.textAlignment = NSTextAlignmentCenter;
infoLabel.tag = 101;
infoLabel.text = [NSString stringWithFormat:@"%@", info.name];
[cell.contentView addSubview:infoLabel];
}
else {
UILabel *theLabel = (UILabel *)[cell viewWithTag:101];
theLabel.text = [NSString stringWithFormat:@"%@", info.name];
}

return cell;
}

Also I am still using the code:

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {

FailedBankInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];
UILabel *theLabel = (UILabel *)[cell viewWithTag:101];
theLabel.text = [NSString stringWithFormat:@"%@", info.name];
}

To update the label text when the Core data object changes with the following code:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

UITableView *tableView = self.tableView;

switch(type) {

case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;

case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray
arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray
arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}

I also found sometimes when segueing back to the table view the table doesn't always update so to fix this you can use:

-(void) viewWillAppear {
[self.tableView reloadData];
}

Thanks for all of your input it did help me come to a solution and I hope that this helps anyone else who is experiencing the same issue. I think I have explained it well enough. I'm not an expert by any means. Just another guy trying to build an App :)

Cell text overlaps the editingAccessory when try to delete a row in iOS7

You can hide or remove editingAccesory in editing mode , so there is no overlapping there,

set this,
Sample Image

Screenshot:

Sample Image

Text Overlapping Text That Should've deleted

UITableView reuses the cells. In your case, when the cells are created the first time, you add a UILabel to it. The next time this cell is loaded, UITableView reuses the existing cell and cellForRowAt adds another UILabel to this cell. The proper implementation is to create a custom UITableViewCell and reset the value of all the attributes in cellForRowAt method.

You can try something like below (please note that this is just a rough implementation and it is assumed that you know the basics of ios programming. If that is not the case, I'd recommend researching it a bit):

Add a custom tableview cell

class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var imgViewPlane: UIImageView!
@IBOutlet weak var lblType: UILabel!
@IBOutlet weak var lblTypeAnswer: UILabel!
@IBOutlet weak var lblRego: UILabel!
@IBOutlet weak var lblRegoAnswer: UILabel!
}

Create a dynamic prototype cell in storyboard tableview and link the IBOutlets. Then in your cellForRowAt, do something like this

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomTableViewCell

cell.imgViewPlane.image = planeImage
cell.lblType.text = "Type:"
cell.lblTypeAnswer.text = planeArray[indexPath.row].type
cell.lblRego.text = "Registration:"
cell.lblRegoAnswer.text = planeArray[indexPath.row].rego

return cell
}

tableHeaderView is overlapping cells when adding custom view to subview of container view

The reason your "blue view" is overlapping the cells is because you are constraining its Top to the red view's Bottom, but you're not updating the header view size.

One good approach is to create a UIView subclass to use as your header view. Setup all of its content with proper auto-layout constraints.

Then, in the controller's viewDidLayoutSubviews(), we use .systemLayoutSizeFitting(...) to determine the header view's height and update its frame:

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

// update table header size

guard let headerView = tableView.tableHeaderView else { return }

let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height

var frame = headerView.frame

// avoids infinite loop!
if height != frame.height {
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}

Here is a complete example...

First, our custom view class:

class SampleHeaderView: UIView {

let redView: UIView = {
let v = UIView()
v.backgroundColor = .systemRed
return v
}()
let blueView: UIView = {
let v = UIView()
v.backgroundColor = .systemBlue
return v
}()
let redTopLabel: UILabel = {
let v = UILabel()
v.backgroundColor = .yellow
v.numberOfLines = 0
return v
}()
let redBottomLabel: UILabel = {
let v = UILabel()
v.backgroundColor = .green
v.numberOfLines = 0
return v
}()
let multiLineLabel: UILabel = {
let v = UILabel()
v.backgroundColor = .cyan
v.numberOfLines = 0
return v
}()

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}

func commonInit() -> Void {

// all views will use auto-layout
[redView, blueView, redTopLabel, redBottomLabel, multiLineLabel].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
}

// prevent label vertical compression
[redTopLabel, redBottomLabel, multiLineLabel].forEach { v in
v.setContentCompressionResistancePriority(.required, for: .vertical)
}

// add top and bottom labels to red view
redView.addSubview(redTopLabel)
redView.addSubview(redBottomLabel)

// add multi-line label to blue view
blueView.addSubview(multiLineLabel)

// add red and blue views to self
addSubview(redView)
addSubview(blueView)

// the following constraints need to have less-than required to avoid
// auto-layout warnings

// blue view bottom to self
let c1 = blueView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0)

// labels trailing contraints
let c2 = redTopLabel.trailingAnchor.constraint(equalTo: redView.trailingAnchor, constant: -8.0)
let c3 = redBottomLabel.trailingAnchor.constraint(equalTo: redView.trailingAnchor, constant: -8.0)
let c4 = multiLineLabel.trailingAnchor.constraint(equalTo: blueView.trailingAnchor, constant: -8.0)

[c1, c2, c3, c4].forEach { c in
c.priority = .required - 1
}

NSLayoutConstraint.activate([

// red view top to self
redView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),

// leading / trailing to self
redView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
redView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

// blue view top to red view bottom
blueView.topAnchor.constraint(equalTo: redView.bottomAnchor, constant: 0.0),

// leading / trailing to self
blueView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
blueView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

// top and bottom labels, constrained in red view
// with a little "padding"
redTopLabel.topAnchor.constraint(equalTo: redView.topAnchor, constant: 8.0),
redTopLabel.leadingAnchor.constraint(equalTo: redView.leadingAnchor, constant: 8.0),

redBottomLabel.topAnchor.constraint(equalTo: redTopLabel.bottomAnchor, constant: 8.0),
redBottomLabel.leadingAnchor.constraint(equalTo: redView.leadingAnchor, constant: 8.0),

redBottomLabel.bottomAnchor.constraint(equalTo: redView.bottomAnchor, constant: -8.0),

// multi-line label, constrained in blue view
// with a little "padding"
multiLineLabel.topAnchor.constraint(equalTo: blueView.topAnchor, constant: 8.0),
multiLineLabel.leadingAnchor.constraint(equalTo: blueView.leadingAnchor, constant: 8.0),
multiLineLabel.bottomAnchor.constraint(equalTo: blueView.bottomAnchor, constant: -8.0),

// the less-than-required priority constraints
c1, c2, c3, c4,

])

}
}

and a sample controller:

class TableHeaderViewController: UIViewController {

var sampleHeaderView = SampleHeaderView()

private(set) lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}()

override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)

NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])

sampleHeaderView.redTopLabel.text = "The Red Top Label"
sampleHeaderView.redBottomLabel.text = "The Red Bottom Label, with enough text that is should wrap."
sampleHeaderView.multiLineLabel.text = "This text is for the Label in the Blue View. It is also long enough that it will require word-wrapping. Note that the header updates itself when the frame changes, such as on device rotation."
tableView.tableHeaderView = sampleHeaderView

}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

// update table header size

guard let headerView = tableView.tableHeaderView else { return }

let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height

var frame = headerView.frame

// avoids infinite loop!
if height != frame.height {
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}

}

extension TableHeaderViewController: UITableViewDataSource, UITableViewDelegate {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}

}

Output:

Sample Image

and rotated:

Sample Image

UITableView header is overlapping the content of its cells

The problem is:

v.translatesAutoresizingMaskIntoConstraints = false

When you provide your custom view as a header/footer for the table view section, you don't need to modify the auto resizing mask on the returned view itself. To my best understanding, doing this breaks table view's internal setup.

But if you register a UITableViewHeaderFooterView subclass as the header/footer, you have to consider that option as you are taking control of configuring the entire header/footer.


Now, I suppose your

let v = headerView
. . .
headerConstraints()

codes are correct. If that is so, the above change should work as expected.


Otherwise below is an example you can try:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let v = UIView()

let headingLabel = UILabel(frame: .zero)
headingLabel.translatesAutoresizingMaskIntoConstraints = false
headingLabel.text = "View Header"
headingLabel.font = UIFont.systemFont(ofSize: 30, weight: .heavy)
headingLabel.textColor = .black

let subheadingLabel = UILabel(frame: .zero)
subheadingLabel.translatesAutoresizingMaskIntoConstraints = false
subheadingLabel.text = "Subheader Content"
subheadingLabel.font = UIFont.systemFont(ofSize: 15, weight: .semibold)
subheadingLabel.textColor = .gray

let additionalLabel = UILabel(frame: .zero)
additionalLabel.translatesAutoresizingMaskIntoConstraints = false
additionalLabel.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
additionalLabel.numberOfLines = 0
additionalLabel.font = UIFont.systemFont(ofSize: 15, weight: .regular)
additionalLabel.textColor = .white
let additionalContent = UIView(frame: .zero)
additionalContent.translatesAutoresizingMaskIntoConstraints = false
additionalContent.backgroundColor = .black
additionalContent.addSubview(additionalLabel)
additionalLabel.leadingAnchor.constraint(equalTo: additionalContent.leadingAnchor, constant: 10).isActive = true
additionalLabel.trailingAnchor.constraint(equalTo: additionalContent.trailingAnchor, constant: -10).isActive = true
additionalLabel.topAnchor.constraint(equalTo: additionalContent.topAnchor, constant: 10).isActive = true
additionalContent.bottomAnchor.constraint(equalTo: additionalLabel.bottomAnchor, constant: 10).isActive = true

let stackView = UIStackView(frame: .zero)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fill
stackView.spacing = 8
stackView.addArrangedSubview(headingLabel)
stackView.addArrangedSubview(subheadingLabel)
stackView.addArrangedSubview(additionalContent)
v.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: v.leadingAnchor, constant: 16).isActive = true
stackView.trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: -16).isActive = true
stackView.topAnchor.constraint(equalTo: v.topAnchor, constant: 16).isActive = true
stackView.bottomAnchor.constraint(equalTo: v.bottomAnchor, constant: -16).isActive = true

return v
}

And don't forget (if you need automatic height calculation):

override func viewDidLoad() {
super.viewDidLoad()
. . .
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 100
. . .
}


Related Topics



Leave a reply



Submit