Using UICollectionView I add a Label to a cell as a subView with the scroll direction set to horizontal. Inside the Label I add a button whose background is an image. For some odd reason if I scroll the view in one direction and then come back the buttons image seems to either have left remnants to the right of the button. Either that or their is another button slightly shifted to the right under the initial buttons. I have realized the more I played around with the buttons the further right they shift. Any assistance would be appreciated
The issue does not seem to occur if the label is shorter
var padding = String(count: 2, repeatedValue: (" " as Character))
let newLabel = UILabel(frame: CGRectZero)
newLabel.autoresizingMask = .FlexibleHeight
newLabel.backgroundColor = UIColor(red: 56/255, green: 143/255, blue: 212/255, alpha: 1)
newLabel.text = "\(padding)\(title)\(padding)"
newLabel.textColor = UIColor.whiteColor()
let fontName: CFStringRef = "Superclarendon-Regular"
newLabel.font = CTFontCreateWithName(fontName, 15, nil)
newLabel.adjustsFontSizeToFitWidth = true
newLabel.clipsToBounds = true
newLabel.layer.cornerRadius = 4
//Fit TO Text
newLabel.numberOfLines = 1
newLabel.sizeToFit()
//Add Button
if let image = UIImage(named: "Nav_Button_X"){//?//.CGImage
let button = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageWidth = image.size.width
var imageHeight = image.size.height
//Resize label for button
var oldLabelFrame = newLabel.frame
var buttonHeight = self.frame.height - self.sectionInsets.top - self.sectionInsets.bottom
var buttonWidth = newLabel.frame.width + imageWidth
//newLabel.frame = CGRect(x: oldLabelFrame.origin.x, y: oldLabelFrame.origin.y, width: oldLabelFrame.width, height: bh)
newLabel.frame.size = CGSize(width: buttonWidth, height: buttonHeight)
button.frame = CGRectMake(newLabel.frame.width - imageWidth, 0, imageWidth, newLabel.frame.height)
button.setBackgroundImage(image, forState: UIControlState.Normal)
newLabel.addSubview(button)
}
return newLabel
}
EDIT:
I have tried recreating the view using only a button and NSMutableAttributedString for styling, which may or may not be a better solution shrugs, and the issue persists so it may not be an issue of how I construct the button. Are there suggestions?
I subclass and set up the UICollectionView like so
let flowLayout:UICollectionViewFlowLayout = UICollectionViewFlowLayout();
flowLayout.scrollDirection = UICollectionViewScrollDirection.Horizontal
super.init(frame: CGRectMake(0, 0, width, height), collectionViewLayout: flowLayout);
self.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier);
self.autoresizingMask = UIViewAutoresizing.FlexibleWidth
self.backgroundColor = UIColor.whiteColor()
self.bounces = true
self.layer.cornerRadius = 5
self.scrollEnabled = true
self.delegate = self;
self.dataSource = self;
self.backgroundColor = UIColor.whiteColor();
and
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell;
cell.backgroundColor = UIColor.clearColor();
let cellItem = subCategories[indexPath.row]
cell.contentView.addSubview(cellItem)
// Configure the cell
return cell
}
Another interesting trait is that I removed the button from odd numbered labels and found that when I perform the action to get the issue the buttons appear on all of the labels
This looks like your cells are being reused and they show views from old cells.
When you call dequeueReusableCellWithReuseIdentifier, the method returns a cell that is not visible anymore in the UICollectionView, but it doesn't clear it's content.
Let's say you have cells A, B and C visible, you add subviews to all of them (with cell.contentView.addSubview(cellItem)). When you scroll until cell A isn't visible anymore, and you call dequeueReusableCellWithReuseIdentifier to get a new cell (call it D), you'll reuse cell A, so it'll have the subviews you added before to cell A, and the subviews you add now to cell D.
To get rid of this, you have two options:
If you're using a custom UICollectionViewCell, you should override prepareForReuse method. There you should remove all contentView's subviews.
If you're using a UICollectionViewCell, you should remove all contentView's subviews before calling cell.contentView.addSubview(cellItem)
If you want to see if you're adding the button multiple times to reused cells, you can reproduce the issue and use the View Hierarchy inspector:
I created a small project to illustrate the issue of "dirty" cells: https://github.com/lucaslt89/DirtyUICollectionViewCellsExample
In my case, I created the view hierarchy in the init method of the cell.
When scrolling slowly, the cells along with the multiline labels would appear perfectly. However, when scrolling too quickly, labels would have the size of the reused cell's label, thus appearing very bad.
I was setting preferredMaxLayoutWidth correctly, but nevertheless the cells would have issues on fast scrolling.
My problem was solved by calling setNeedsLayout inside prepareForReuse:
override func prepareForReuse() {
setNeedsLayout()
super.prepareForReuse()
}
Related
I'm building an app with a messenger like interface. I use a tableView to accomplish this. Each cell contains a UIView - the message bubble and a UILabel - the message that is nested in the UIView.
It works great on texts of small sizes but for some reason when the UILabel is supposed to break lines it doesn't and it all is in one line. The amount of lines is set to zero.
This is my message handling class:
func commonInit() {
print(MessageView.frame.height)
MessageView.clipsToBounds = true
MessageView.layer.cornerRadius = 15
myCellLabel.numberOfLines = 0
let bubbleSize = CGSize(width: self.myCellLabel.frame.width + 28, height: self.myCellLabel.frame.height + 20)
print(bubbleSize.height)
MessageView.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: bubbleSize.width, height: bubbleSize.height)
if reuseIdentifier! == "Request" {
MessageView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner, .layerMinXMaxYCorner]
MessageView.backgroundColor = UIColor(red: 0, green: 122/255, blue: 1.0, alpha: 1.0)
} else {
MessageView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner]
MessageView.backgroundColor = UIColor.lightGray
}
}
Cell calling function:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if queryCounter % 2 == 0 && indexPath.row % 2 == 0{
cellReuseIdentifier = "Answer"
} else {
cellReuseIdentifier = "Request"
}
let cell:MessageCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MessageCell
cell.myCellLabel.textColor = UIColor.white
cell.myCellLabel.text = self.messages[indexPath.row]
let height = cell.myCellLabel.text!.height(withConstrainedWidth: cell.myCellLabel.frame.width, font: cell.myCellLabel.font)
print(height)
cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1)
return cell
}
The height variable is calculated based on text size. It shows that the text size is calculated normally - accounting for line break.
I was unable to modify the cell height based on this calculation - nothing I tried works.
I think it might be a constraints issue.
My Constraints:
How do I make the lines break? Please help.
EDIT: I just notice that the MessageView.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: bubbleSize.width, height: bubbleSize.height) has no affect what so ever on the message bubbles.
Setting the frame while using autolayout won't work.
I can't say what exactly happens here without the entire context, but some common pitfalls when reusing cells and autolayout are:
Forgetting to set automatic height for your cells (expanding cell in a storyboard manually will override this setting)
Tableview also needs estimatedHeight sometimes to work properly
Sometimes you need to call setNeedsLayout after you add content to
the cell
Check the console and if there are some warnings about breaking constraints, you can easily find issues there.
Try to find the label height based on label width and text font and then set your label height constraint to that.
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)
return ceil(boundingBox.height)
}
}
something like this:
let textHeight = yourtext.height(withConstrainedWidth: yourlabel.frame.width, font: font)
yourLabel.heightAnchor.constraint(equalToConstant: textHeight).isActive = true
After multiple hours of trying everything I managed to fix it. The problem was with the constraints.
1. As you can see the in this old layout. The UIView was constrained everywhere except the left -> that's where the text goes.
The commonInit() method of the UITableViewCell was called before any text was initialized. That's not good because all of the cell resizing is based on text which was not yet passed to the cell -> Move the method after cell initialization.
let cell:MessageCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MessageCell
cell.myCellLabel.text = self.messages[indexPath.row]
//Before calling commonInit() we need to adjust the cell height.
let height = cell.myCellLabel.text!.heightForView(text: cell.myCellLabel.text!, font: cell.myCellLabel.font, width: self.view.frame.width / 2)
// Then we set the width of the UILabel for it to break lines at 26 characters
if cell.myCellLabel.text!.count > 25 {
tableView.rowHeight = height + 20
cell.myCellLabel.widthAnchor.constraint(equalToConstant: cell.frame.width / 2).isActive = true
cell.updateConstraints()
}
// Calling commonInit() after adjustments
cell.commonInit()
cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1)
return cell
Then we need to update the constraints so that the UIView and UILabel resize with the cell height.
Done. Now it works as needed. Thank you for all of the suggestions!
Introduction
Context:
I'm creating one of my first apps but I've ran into an issue I cannot figure out.
I have tableView with cells packed with quite a few UIElements. All constraints are done using the EasyPeasy library which basically just sets auto layout constraints ( I have tried setting them manually also). The UITextView in question is constrained by various numbers to the left, right, top and bottom, I have no constraints on it for height or width.
in cellForRowAt indexPath: I set the textView delegate for each cells textView to self, using a delegate property declared within the cells custom class. I also tag every textView with its cells indexPath.row (gives textView.tag integer in textViewDidChange method).
Issue/acknowledgments:
After browsing SO a lot I've found a few questions alike this but I have not been able to make them work for me, I have implemented parts of them that felt logic to my case. I believe the problem differencing my situation from those questions lies in that for my cell design to work the cells has to have a height of itemHeight or higher.
I have noticed that as I type into the textview the textview itself increases in height (even below the cells border but its not visible as it reaches that point), however the cell itself doesn't resize.
I've tried with a cell that only contains a textView so the problem must lie in the textViewDidchange or heightForRowAt indexPath methods.
Question:
What am I doing wrong here? Why doesn't the cells height change dynamically as I type in the textView?
Code:
func textViewDidChange(_ textView: UITextView) {
var newframe = textView.frame
newframe.size.height = textView.contentSize.height - textView.frame.size.height + itemHeight[textView.tag]
textView.frame = newframe
let ndxPath = IndexPath(row: textView.tag, section: 0)
let cell = tableView.cellForRow(at: ndxPath) as! EventsCell
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: cell.frame.width, height: textView.frame.height)
tableView.beginUpdates()
tableView.setNeedsLayout() //have tried without this line
tableView.layoutIfNeeded() //have tried without this line
tableView.endUpdates()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if UITableViewAutomaticDimension > itemHeight[indexPath.row] {
return UITableViewAutomaticDimension
} else {
return itemHeight[indexPath.row]
}
}
TextView constraints:
let containerView : UIView = {
let cv = UIView(frame: .zero)
cv.backgroundColor = .white
cv.translatesAutoresizingMaskIntoConstraints = false
cv.layer.cornerRadius = 7
return cv
}()
let eventText : GrowingTextView = { // GrowingTextView is a extension to a regular UITextView
let tv = GrowingTextView()
tv.allowsEditingTextAttributes = true
tv.isScrollEnabled = false
var delegate: UITextViewDelegate?
tv.textContainerInset = UIEdgeInsetsMake(1, 1, 0, 1)
tv.autoresizingMask = .flexibleHeight
tv.translatesAutoresizingMaskIntoConstraints = false
return tv
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
containerView.addSubview(eventText)
contentView.addSubview(containerView)
containerView .easy.layout([Height(CGFloat(95 * itemCount)), Left(8), Right(8)])
eventText .easy.layout([Left(77), Right(5), Top(90), Bottom(4)])
}
Thanks for reading my post.
The constraints that determine the height should be laid out in such a way that the textView is attached directly to the top and bottom of the contentView or to views which are connected to the top and bottom of the contentView, so that autolayout can make out the height by connecting the constraints.
Make sure that you do not mention a height for the textView and disable scrolling. Let automatic dimension take care of all that.
Now all you need to do is call tableView.beginUpdates() and tableView.endUpdates() on textViewDidChange
Here is my repo which demonstrates the same.
OP Edit:
You should store the additional height that you add in a variable in the cell class so the cells can reload an appropriate height when the tableVIew is reloaded.
You should also change textViewDidChange method
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: cell.frame.width, height: textView.frame.height)
to
let newFrame = ”originalCellHeight” - ”originalTextViewHeight” + textView.contentSize.height
cell.frame = CGRect(x: cell.frame.origin.x, y: cell.frame.origin.y, width: cell.frame.width, height: newFrame )`
I have a long string:
"A very long string"
that won't be displayed fully in a UITableView, i.e. it gets truncated to:
"A very long s..."
Is it possible to have this text in a UITableViewCell automatically scroll horizontally and indefinitely so that it loops around? Perhaps this can be achieved using an attributed string?
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
cell.textLabel?.text = "A very long string"
cell.textLabel?.textAlignment = .center
Thanks in advance for any help with this!
EDIT (my attempt at a solution proposed)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
let frame = CGRect(x: 0.0, y: 0.0, width: cell.bounds.width * 2.0, height: cell.bounds.height)
let scrollView = UIScrollView(frame: frame)
cell.addSubview(scrollView)
let label = UILabel(frame: scrollView.bounds)
scrollView.addSubview(label)
label.text = "some very long text that doesn't quite fit"
label.textAlignment = .center
let newFrame = CGRect(x: label.frame.width, y: 0, width: label.frame.width, height: scrollView.frame.height)
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse], animations:( { void in
scrollView.scrollRectToVisible(newFrame, animated: true)}),
completion: ({ completed in }))
}
Scrollview inside a table view is a bad idea. Instead you can enable the automaticDimension and estimated row height and tableView will manage the cell height automatically.
self.tableView.estimatedRowHeight = 44
self.tableView.allowsMultipleSelection = true
So as per the content each cell will manage its own height.
Hope this would be helpful!
You would have to make a custom tableview cell, which has a custom scrollview which handles UILabel scrolling. You would need to assign the text and measure the width. If it is wider than the containing view then add a second duplicate label to its right. You then need to animate the scrollview to the left by the width of the label. On complete, jump back to offset 0,0 and animate again.
Usually, to have special effect as such will require customization of an UILabel. To make my life easier, I will always try to search if there is already a working library out there for me to consume.
A simple Google search brings me MarqueeLabel, hope it helps!
I created a sample app here: https://github.com/steffimueller/LTNavigationBar-TestProject
When you pull down the table at the top above the image a white background appears. This should not be the case. The header image should always be bound at the top of the screen. Here is how it looks in my case:
and here is how it should look like:
The former screenshot is from this app using the parallax branch of the git repo. It is the first tab in the parallax demo which has the effect I want.
Here is the code I use to create the table:
class Page1ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var topView:UIView!
var topImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setLeftBarButtonItem(UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "sdfsdf"), animated: true)
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIndentifier)
self.navigationController?.navigationBar.lt_setBackgroundColor(UIColor.clearColor())
tableView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleWidth
topView = UIView(frame: CGRectMake(0, 0, 320, 212))
topView.contentMode = UIViewContentMode.ScaleToFill
topView.autoresizesSubviews = true
topView.backgroundColor = UIColor.yellowColor()
topView.clipsToBounds = true
tableView.tableHeaderView = topView
let img = UIImage(named: "bg")
topImageView = UIImageView(image: img)
topImageView?.frame = CGRectMake(0, -89, UIScreen.mainScreen().bounds.size.width, 307)
topImageView?.contentMode = UIViewContentMode.ScaleToFill
topView.addSubview(topImageView!)
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let color: UIColor = UIColor(red: 0/255, green: 175/255, blue: 240/255, alpha: 1)
let offsetY:CGFloat = scrollView.contentOffset.y
if offsetY > NAVBAR_CHANGE_POINT {
let alpha:CGFloat = 1 - ((NAVBAR_CHANGE_POINT + 64 - offsetY) / 64)
self.navigationController?.navigationBar.lt_setBackgroundColor(color.colorWithAlphaComponent(alpha))
}
else {
self.navigationController?.navigationBar.lt_setBackgroundColor(color.colorWithAlphaComponent(0))
}
if offsetY < 0 {
let progress:CGFloat = fabs(offsetY) / 300
self.topImageView?.transform = CGAffineTransformMakeScale(1 + progress, 1 + progress)
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.view).offset(-64)
}
}
}
Edit: I don't want to set tableView.bounces = false.
How can I stick the TableHeader Image always on top of the Screen like in my second screenshot?
There are lots of ways to do this.
Do you want the topView to scroll with the table view? If so, instead of adding the topView as a subview, just assign it to the table view's tableHeaderView property.
Don't want it to scroll? Add the topView to the view controller's view, not the table view. Then position the table view below the top view in the nib/storyboard.
Want the table view to scroll over the image view as the user scrolls down? Put the image view behind the table view, make the table view transparent, and make the table view's contentInset start the first cell below the image.
I'm guessing just messing with the contentInset's top value will get you what you want here.
It's not clear why both the topView and the topImageView are needed. It looks like just the topImageView would be sufficient.
Just add a frame to your tableView like this:
self.tableView.frame=CGRectMake(0, YYY, 320, 307)
where YYY is the Y position you want the table to be positioned at
UPDATE:
Based on your updated question, if you want to have the image appear to be "stuck' to the top of the tableview, even when the user pulls the table down, you need to use the bounds of the tableView to set the frame of your image. Use something like this to set the image frame:
UIImageView *myImage = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.tableView.bounds.size.height, self.view.frame.size.width, 100.0f+self.tableView.bounds.size.height)];
[self.tableView addSubview:myImage];
You need to make sure your image is larger then the visible headerView so it will continue to show as the user pulls the table down.
The "100.0f+" part is just adding height to the image so it will show
into the headerView, adjust this to fit your image and tableView
header.
UPDATE 2
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView.delegate=self;
self.myTableView.dataSource=self;
UIImageView *myImageView=[[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.myTableView.bounds.size.height, self.view.frame.size.width, 300.0F+self.myTableView.bounds.size.height)];
[myImageView setClipsToBounds:TRUE];
[myImageView setImage:[UIImage imageNamed:#"myImage.jpg"]];
[self.myTableView addSubview:myImageView];
}
You have to use the following in viewDidLoad():
topView = UIView(frame: CGRectMake(0, 0, 320, 300))
let testView = UIView(frame: CGRectMake(0, 0, 320, 200))
testView.backgroundColor = UIColor.clearColor()
tableView.tableHeaderView = testView
let img = UIImage(named: "bg")
topImageView = UIImageView(image: img)
topImageView?.frame = CGRectMake(0, 0, 320, 180)
topView.addSubview(topImageView!)
topImageView?.contentMode = UIViewContentMode.ScaleToFill
tableView.addSubview(topView!)
tableView.sendSubviewToBack(topView!)
Try by adding an ImageView just below the UITableView(background color should be clearColor) and setting the
yourTableView.contentInset = UIEdgeInsetsMake(100.0, 0.0, 0.0, 0.0).
If you want to bound the UIImage to top of the screen then do the following things. Disable Bounce property of UITableView. you can disable that property from xib or programmatically as well. here i write the code for disable Bounce property programmatically in swift.
self.tableView.bounces = false
Hope this help you.
What i did in my case is to use tableHeaderView instead of section header like, myTableView.tableHeaderView = myImageView
and then in scrollViewDidScrollMethod,
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0
{
scrollView.layoutIfNeeded()
var headerFrame = myTableView.tableHeaderView?.frame
headerFrame?.origin.y = scrollView.contentOffset.y
headerFrame?.size.height = headerHeight-scrollView.contentOffset.y //headerHeight is a constant for actual height of header on storyboard
myTableView.tableHeaderView?.frame = headerFrame!
}
}
Hope this helps in your case
I have a tableView using IB with custom cells and prototype cells.
I'm trying to make the cells a little shorter in width than the tableView.frame to leave a little space between the left and right corners.
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
cell.bounds = CGRectMake(10, self.tableView.frame.origin.y, 30, self.tableView.frame.size.height)
cell.layer.bounds = CGRectMake(10, self.tableView.frame.origin.y, 30, self.tableView.frame.size.height)
cell.textLabel?.bounds = CGRectMake(10, self.tableView.frame.origin.y, 30, self.tableView.frame.size.height)
Update: here is a good example explaining how to add a subView to your tableView.
http://natashatherobot.com/ios-frame-vs-bounds-resize-basic-uitableview-cell/
Update 2: Looks like there isn't an easy way to do this. There are 3 ways of achieving this as far as I know:
Add a rounded and a shorter image to your cell that has the same exact color and matches your background.
You could subclass tableViewCell and then play with the layoutSubviews, this way you can make it shorter before it draws the cell. I've done it but the scrolling performance sucks.
The best way is to ditch the tableView altogether and re-do it with a collectionView.
The cells in the tableview are supposed to be as wide as their container.
If you need your cells to have a different width than the table view, I would suggest adding a view as subview to cell.contentView and make that view as wide as you need while making sure the contentView has clear background and no separator and all (so that it appears it is not there).
Another solution would be to have the tableView not as wide as it's superview by adding the left/right padding to it. But the you would have the issue that on the left and right side, where the padding is, you won't be able to scroll the tableView
I consider the cleanest solution to use a collectionView. It is not that much different than a tableView and you can configure the entire size of the cell, not just the height.
Hope this helps you fix your problem. Let me know if you need more help.
Swift 3:
For people still looking for a good solution, there's an easier and more effective alternative to using layoutSubviews or re-doing the whole thing with collectionView.
If you have already subclassed the tableViewCell, then in your TableViewController class you can add this to add a plain white border to each side of the table view cell.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// make sure in storyboard that your cell has the identifier "cell" and that your cell is subclassed in "TableViewCell.swift"
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
// the following code increases cell border on all sides of the cell
cell.layer.borderWidth = 15.0
cell.layer.borderColor = UIColor.white.cgColor
return cell;
}
If you want to add different sized borders to each side of the cell, you can also do this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
// the following code increases cell border only on specified borders
let bottom_border = CALayer()
let bottom_padding = CGFloat(10.0)
bottom_border.borderColor = UIColor.white.cgColor
bottom_border.frame = CGRect(x: 0, y: cell.frame.size.height - bottom_padding, width: cell.frame.size.width, height: cell.frame.size.height)
bottom_border.borderWidth = bottom_padding
let right_border = CALayer()
let right_padding = CGFloat(15.0)
right_border.borderColor = UIColor.white.cgColor
right_border.frame = CGRect(x: cell.frame.size.width - right_padding, y: 0, width: right_padding, height: cell.frame.size.height)
right_border.borderWidth = right_padding
let left_border = CALayer()
let left_padding = CGFloat(15.0)
left_border.borderColor = UIColor.white.cgColor
left_border.frame = CGRect(x: 0, y: 0, width: left_padding, height: cell.frame.size.height)
left_border.borderWidth = left_padding
let top_border = CALayer()
let top_padding = CGFloat(10.0)
top_border.borderColor = UIColor.white.cgColor
top_border.frame = CGRect(x: 0, y: 0, width: cell.frame.size.width, height: top_padding)
top_border.borderWidth = top_padding
cell.layer.addSublayer(bottom_border)
cell.layer.addSublayer(right_border)
cell.layer.addSublayer(left_border)
cell.layer.addSublayer(top_border)
return cell;
}
Hope this helps.
I found the other answers unhelpful/incorrect, but found what I needed in one of the answers here:
How to set the width of a cell in a UITableView in grouped style
The key is, if you have a custom cell (which OP has, and most apps would have anyway), then you can override the setFrame method to whatever width you need. No need to redesign your app or do anything tricky.