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,
Screenshot:
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 IBOutlet
s. 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:
and rotated:
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
Crop Image According to Rectangle in Swiftui
Create CSV File in Swift and Write to File
Swift 3/Macos: Open Window on Certain Screen
Use Different Googleservice-Info.Plist for Single Project in Xcode Using Swift4
Swiftlint Overriding Project Settings Related to Spm
Does Untimeintervalnotificationtrigger Nexttriggerdate() Give the Wrong Date
Why Does Type(Of:) Return Metatype, Rather Than T.Type
Shared Cookies with Wkprocesspool for Wkwebview in Swift
How to Get a Double Value Up to 2 Decimal Places
Why Do I Get "Static Member '...' Cannot Be Used on Instance of Type '...'" Error
Alamofire 5 Upload Encodingcompletion
Swiftui MACos Scroll a List with Arrow Keys While a Textfield Is Active
Gmsplace Returns Invalid Coordinate (-180, -180), But Name and Place Id Are Correct
Xcode Swift. How to Programmatically Select Cell in View-Based Nstableview
How to Make Sfspeechrecognizer Available on MACos
Swift/Scenekit Problems Getting Touch Events from Scnscene and Overlayskscene