Varying separator colours in UITableView - swift

I have an account page in my app where I would like to have account information displayed above the first section in a tableview, similar to the Apple ID page in the settings.
My question is based around how to structure the view such that there is no separator shown above the profile picture. Or to make it that the information appears to be within the tableview but not within a section.
From reading around it seems either not possible or very difficult to have some separators showing and others not.
In the case of the apple ID page, is that picture, name and email in its own section or a cell that is part of the section beneath it?
The following picture is the closest I have got although that top separator proves difficult to hide.
Apologies for the broad nature of the question, however i think the aim is clear and once a solution is reached I will make the question more specific for future viewers.
Thanks.

Try this
//This will hide seperator for all rows
tableView.separatorStyle = UITableViewCellStyle.None
//For custom seperator
if indexPath.row != 0{
var aView: UIView = UIView()
aView.frame = CGRect(x:0,y:44,width:300,height:1)
aView.backgroundColor = UIColor.redColor()
cell .addSubview(aView)
}

You need to use custom view in your UITableViewCell for that.
1. Set UITableView Separator to none in storyboard.
2. Create a custom UITableViewCell with a UIView in it.
class TableCell: UITableViewCell
{
#IBOutlet weak var separatorView: UIView!
}
3. Set the backgroundColor of separatorView in cellForRowAt according to your requirement.
Example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableCell
switch indexPath.row
{
case 0:
cell.backgroundColor = .red
case 1:
cell.backgroundColor = .green
case 2:
cell.backgroundColor = .blue
case 3:
cell.backgroundColor = .yellow
default:
cell.backgroundColor = .orange
}
return cell
}
Screenshot:

Thanks Dev and PGDev, I was able to come up with a nice solution that worked well for static cells. I made an extension for UITableViewCell that looks like this.
let separatorColour = UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 0.5)
extension UITableViewCell {
func separator(topInset: CGFloat, btmInset: CGFloat, showTop: Bool, showBtm: Bool) {
if showTop {
let topSeparator: UIView = UIView()
topSeparator.frame = CGRect(x: topInset, y: 0, width: frame.width, height: 0.5)
topSeparator.backgroundColor = separatorColour
addSubview(topSeparator)
}
if showBtm {
let btmSeparator: UIView = UIView()
btmSeparator.frame = CGRect(x: btmInset, y: frame.height - 0.5, width: frame.width, height: 0.5)
btmSeparator.backgroundColor = separatorColour
addSubview(btmSeparator)
}
}
func btnSeparator() {
let topSeparator: UIView = UIView()
topSeparator.frame = CGRect(x: 0, y: 0, width: frame.width, height: 0.5)
topSeparator.backgroundColor = separatorColour
addSubview(topSeparator)
let btmSeparator: UIView = UIView()
btmSeparator.frame = CGRect(x: 16, y: frame.height - 0.5, width: frame.width - 32, height: 0.5)
btmSeparator.backgroundColor = separatorColour
addSubview(btmSeparator)
}
}
In the first function separator() you can simply choose the inset for top and bottom separator and whether they are visible or not.
TopCell
So in the case of the cell with the name label shown in the picture above, both top and bottom separators are visible. However because it is supposed to appear as the first cell in the section, it has no top inset and an appropriate bottom inset.
MiddleCell
In the case of the cell with the email label it only has the lower separator (with inset) visible as the cell above has a bottom separator.
BottomCell
For the cell with the password label, it only has the lower separator (without offset) visible as the cell above has a bottom separator.
SpecialCase
The btnSeparator() function was just for the case of the help and support button, where I wanted inset on both sides.
Now to implement this extension, I used a switch statement inside the cellForRowAt function to give each cell the appropriate extension and this is what that looks like:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath)
let inset: CGFloat = 16
switch indexPath {
// First Section
case [0,1]:
cell.separator(topInset: 0, btmInset: inset, showTop: true, showBtm: true)
case [0,2]:
cell.separator(topInset: inset, btmInset: inset, showTop: false, showBtm: true)
case [0,3]:
cell.separator(topInset: inset, btmInset: 0, showTop: false, showBtm: true)
// Second Section
case [1,0]:
cell.separator(topInset: 0, btmInset: inset, showTop: true, showBtm: true)
case [1,1]:
cell.separator(topInset: inset, btmInset: inset, showTop: false, showBtm: true)
case [1,2]:
cell.separator(topInset: inset, btmInset: 0, showTop: false, showBtm: true)
// Third Section
case [2,0]:
cell.btnSeparator()
case [2,1]:
cell.separator(topInset: inset, btmInset: 0, showTop: false, showBtm: true)
case [2,3]:
cell.separator(topInset: 0, btmInset: 0, showTop: true, showBtm: true)
default:
print("default")
}
return cell
}
Be sure to set separator to none in the Storyboard or use:
tableView.separatorStyle = UITableViewCellStyle.None

Related

UICollection Cells are mixed in swift

I'm trying to generate cells and put labels inside of it. However, when i scroll down labels got mixed between cells. Here is my code and i'm trying to solve it.
let lblTitle = UILabel()
let lblMetro = UILabel()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell
if indexPath.row == 0 {
lblTitle.frame = CGRect(x: 0, y: 0, width: 195, height: 40)
lblTitle.font = UIFont.systemFont(ofSize: 14)
lblTitle.textColor = UIColor.white
lblTitle.backgroundColor = colorLiteral(red: 0.2122299671, green: 0.4379466176, blue: 0.8993332386, alpha: 1)
lblTitle.text = " 1”
cell?.contentView.addSubview(lblTitle)
}
if indexPath.row == 1 {
lblMetro.frame = CGRect(x: 55, y: 290, width: 100, height: 20)
lblMetro.font = UIFont.boldSystemFont(ofSize: 17)
lblMetro.textColor = colorLiteral(red: 0, green: 0.3117707968, blue: 0.5609284043, alpha: 1)
lblMetro.text = “2”
cell?.contentView.addSubview(lblMetro)
}
return cell ?? UICollectionViewCell()
}
}
Here cells are dequeued
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MenuCell", for: indexPath) as? UICustomCollectionViewCell
so you might get a 1 with previously added label , you need to clear them after dequeue , it would be messy but it's better to isolate the vc's labels from the cells 1 so add them inisde the cell configure or make them an outlets , to remove give them a tag and after the above line do
cell.contentView.subviews.forEach {
if $0.tag == 200 {
$0.removeFromSuperview()
}
}
Not optimised but this might solve it, remove the subview from superview before adding it:
cell?.contentView.lblTitle.removeFromSuperview()
cell?.contentView.addSubview(lblTitle)
And:
cell?.contentView.lblMetro.removeFromSuperview()
cell?.contentView.addSubview(lblMetro)
I suggest using a very rarely used method of UICollectionViewCell or UITableViewCell prepareForReuse.
In definition of UICustomCollectionViewCell insert the function:
class UICustomCollectionViewCell: UICollectionViewCell {
func prepareForReuse() {
// This method is immediately called when a cell is about to be dequeued.
super.prepareForReuse()
if let view = contentView.viewWithTag(100) {
view.removeFromSuperView()
}
if let view = contentView.viewWithTag(101) {
view.removeFromSuperView()
}
}
}
Then give tags to the labels
lblMetro.tag = 100
lblTitle.tag = 101
This solution is efficient if you only use a limited labels and cells. For a more generic approach create labels dynamically and share the tag. In the prepareForReuse() just remove subview with that tag.

Changing the UIImage position programmarically

I have a UITableView with sections. I added a UImage in each cell, but it is on the left side, how can I change it to make it go to the right side?
this is my code for each cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cells")
cell?.textLabel?.text = data[indexPath.section].subType[indexPath.row]
cell?.textLabel?.textAlignment = .right
cell?.textLabel?.font = UIFont(name: "GESSTwoLight-Light", size: 15)
cell!.isUserInteractionEnabled = false;
cell!.imageView?.image = UIImage(named: "username.png")
return cell!
}
This is my UITableView
you simply have to do the following in the tableView(_:cellForRowAt:) method:
cell.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
cell.textLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
cell.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
This will create the following
Credits goes to liau-jian-jie for sharing this solution, which also can be applied to UIButtons.

Making UITableView drop down with stackview

I am trying to achieve a UITableView drop down when I click on a button. Initially the tableView should be hidden, and when user presses button, it should drop down. I have been trying to achieve this with a UIStackView but to no success. Maybe I am doing it wrong or maybe there is another approach do do this.
let stackView = UIStackView()
var btn: UIButton!
var myTableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
myTableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 1))
myTableView.frame = CGRect(x: 0, y: 0, width: 300, height: 200)
myTableView.center = CGPoint(x: self.view.frame.width/2, y: self.view.frame.height/2)
myTableView.showsVerticalScrollIndicator = false
btn = UIButton(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50))
btn.backgroundColor = UIColor.blue.withAlphaComponent(0.3)
btn.setTitle("DropDownMenu", for: UIControlState.normal)
btn.titleLabel?.textColor = .white
btn.titleLabel?.textAlignment = .center
btn.center = CGPoint(x: self.view.frame.width/2, y: myTableView.center.y - myTableView.frame.height/2 - btn.frame.height/2)
btn.addTarget(self, action: #selector(btnPressed), for: UIControlEvents.touchUpInside)
stackView.axis = UILayoutConstraintAxis.vertical
stackView.distribution = UIStackViewDistribution.equalSpacing
stackView.alignment = UIStackViewAlignment.center
stackView.spacing = 16.0
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(btn)
stackView.addSubview(myTableView)
self.view.addSubview(stackView)
}
#objc func btnPressed() {
UIView.animate(withDuration: 1) {
self.myTableView.isHidden = !self.myTableView.isHidden
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "This is cell " + indexPath.row.description
cell.textLabel?.textColor = .black
return cell
}
I can get the tableView to disappear but with no animations. Any thoughts?
The approach I ended up taking was not going via a UIStackView but insead simply having a button that animates the tableView's frame. The frame is initially set to the width of the screen and a height of 0. When user presses button, I set the height.
UIView.animate(withDuration: 0.25, animations: {
self.menuTable.frame = CGRect(x: 0, y: (sender.center.y + sender.frame.height/2), width: self.view.frame.width, height: yourHeight)
})

How to change UITableView section borders?

I have a UITableView and I simply want to set the same width to all separators. I already know something about customizing uitableview, but this one is a mystery to me..
Basically, I want to set this section headers separators to be the same width as cell separators, have the same leading space to the superview. What's the best way to do it?
EDIT
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header = view as! UITableViewHeaderFooterView
header.textLabel?.font = header.textLabel?.font.withSize(12)
header.textLabel?.textColor = ColorConstants.symbolsColor
}
In the table view method CellForRowAt...
Try adding this code
cell.preservesSuperviewLayoutMargins = false
cell.seperatorInset = UIEdgeInsets.zero
cell.layoutMargins = UIEdgeInsets.zero
Headers and footers don't have separator lines but you can mock them using custom UIViews and putting them in:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 50))
let px = 1 / UIScreen.main.scale
let frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: px)
let line = UIView(frame: frame)
view.addSubview(line)
line.backgroundColor = self.tableView.separatorColor
self.tableView.tableHeaderView = view
}
And add your other code to this function as well instead of willDisplayHeaderView.
You may need to adjust the CGRect dimensions to match what you want exactly. I believe the default cell separators start at 15 points (not 100% sure on that)
credit to: https://stackoverflow.com/a/28177407/7535093

How to apply custom styling to NSTableHeaderView?

So I am going for a custom looking NSTableView. I've already successfully subclassed NSTableRowView and NSTextFieldCell to achieve the look I'm going for, however I'm struggling of getting rid of the default styling for the header. I seem to be able to tweak its frame, however I'm not sure where the rest of the default styling is coming from.
As you see on the screenshot the red area is the increased frame of the headerView. I'm using its CALayer to set the colour, however how to change the contents inside is beyond me...
Here's what I'm doing in the viewDidLoad of my ViewController
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.wantsLayer = true
tableView.headerView?.frame = NSMakeRect(0, 0, (tableView.headerView?.frame.width)!, 32.00)
tableView.headerView?.wantsLayer = true
tableView.headerView?.layer?.backgroundColor = NSColor.red.cgColor
}
I've also tried subclassing NSTableHeaderView, however this class seems to be extremely limited in terms of the customizations I can make...
Any help would be appreciated?
The table view is view based but the header isn't and the header cells still are class NSTableHeaderCell. Use NSTableColumn's property headerCell. You can set the cell's properties like attributedStringValue and backgroundColor or replace the cells by instances of a subclass of NSTableHeaderCell and override one of the draw methods.
Play around with this to get inside the header.
Remember to except the answer if it works for you.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//Color for the header.
let topColor = UIColor(red: (70/255.0), green: 000/255.0, blue: 000/255.0, alpha: 255)
//Location of label.
let locationOfLabel = self.view.frame.width
let headerView:UIView = UIView()
//Locating the text in the label
let title = UILabel(frame: CGRect(x: 0, y: 30, width: locationOfLabel, height: 21))
title.textAlignment = .center
//Changing the title in the label per the default.
let defaults:UserDefaults = UserDefaults.standard
defaults.synchronize()
let cardSelector = defaults.object(forKey: "selectorKeyID") as! Int
switch (cardSelector) {
case 0: title.text = "Personal"
break
case 1: title.text = "Saved"
break
case 2: title.text = "Favorite"
break
case 3: title.text = "Grouped"
break
default:
break
}
//Coloring the text in the label
//Add the label
title.textColor = UIColor.gray
headerView.addSubview(title)
//Adding a button to the header.
let closeBttn = UIButton(type: UIButtonType.system) as UIButton
closeBttn.frame = CGRect(x: 0, y: 30, width: 90, height: 27)
closeBttn.setTitle("Close", for: UIControlState())
closeBttn.setTitleColor(buttonColor, for: UIControlState())
closeBttn.titleLabel?.font = UIFont.systemFont(ofSize: 19, weight: UIFontWeightMedium)
closeBttn.addTarget(self, action: #selector(MainTableViewController.close), for: UIControlEvents.touchUpInside)
headerView.addSubview(closeBttn)
let menuButton = UIButton(type: UIButtonType.system) as UIButton
menuButton.frame = CGRect(x: locationOfLabel-53, y: 30, width: 27, height: 27)
menuButton.setBackgroundImage(UIImage(named: "VBC Menu4.png"), for: UIControlState())
menuButton.addTarget(self, action: #selector(MainTableViewController.menuButton), for: UIControlEvents.touchUpInside)
headerView.addSubview(menuButton)
//Coloring the header
headerView.backgroundColor = topColor
//Rounding the corners.
headerView.layer.cornerRadius = 10
headerView.clipsToBounds = true
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}