I have a table with two sections. Property textAligment = .right for header sections. But it's too close to the border of the phone. How to indent from the border closer to the center?
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header = view as! UITableViewHeaderFooterView
header.textLabel?.textColor = .black
header.textLabel?.textAlignment = .right
}
Here's a very simple example...
Add a UITableViewController in Storyboard.
Assign its Custom Class to SectionTableViewController (from the code below).
Use the default cell.
Give the cell an Identifier of "DefCell"
Run the app. This is what you should see:
I've constrained the label to use contentView.layoutMarginsGuide and set the trailing constant to -32
class MySectionHeaderView: UITableViewHeaderFooterView {
let myLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textColor = .black
v.textAlignment = .right
// during development, give it a background color to make it easy to see the frame
//v.backgroundColor = .cyan
return v
}()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
contentView.addSubview(myLabel)
// use layout margins guide
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
myLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
myLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
myLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
// set the constant to provide more trailing space as desired
// Note: must be a negative value
myLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -32.0),
])
}
}
class SectionTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MySectionHeaderView.self, forHeaderFooterViewReuseIdentifier: "MyHeader")
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "DefCell", for: indexPath)
c.textLabel?.text = "Section: \(indexPath.section) Row: \(indexPath.row)"
return c
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: "MyHeader") as? MySectionHeaderView else {
fatalError("Could not dequeue MySectionHeaderView!")
}
v.myLabel.text = "Section \(section)"
return v
}
}
You'll notice these lines in the declaration of myLabel in MySectionHeaderView:
// during development, give it a background color to make it easy to see the frame
//v.backgroundColor = .cyan
This is how it looks when the label has a background color, so you can easily see how the label is laid-out:
Related
I have a tableView with cells that contain UiCollectionView. My didSelect tableView's delegate isn't called when I touch the cell on the collectionView.
I think it's my collectionView that get the touch instead. Do you have any elegant solution to keep the scroll enabled on my collectionView but disable the selection and pass it to the tableview ?
Here is my tableView declaration :
private lazy var tableView:UITableView = { [weak self] in
$0.register(TestTableViewCell.self, forCellReuseIdentifier: "identifier")
$0.delegate = self
$0.dataSource = self
return $0
}(UITableView())
Here are my delegate and dataSource methods:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! TestTableViewCell
return cell
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print(indexPath)
}
And here is my cell :
public class TestTableViewCell : UITableViewCell {
private lazy var collectionViewFlowLayout:UICollectionViewFlowLayout = {
$0.scrollDirection = .horizontal
$0.minimumLineSpacing = 0
$0.minimumInteritemSpacing = 0
return $0
}(UICollectionViewFlowLayout())
private lazy var collectionView:UICollectionView = {
$0.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "identifier")
$0.delegate = self
$0.dataSource = self
return $0
}(UICollectionView(frame: .zero, collectionViewLayout: collectionViewFlowLayout))
public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: cell:"identifier", for: indexPath) as! cell:UICollectionViewCell
return cell
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return collectionView.frame.size
}
}
If you spot any compilation error, excuse me, this is an anonymized copy/past. My app is running without error.
If you want an example of what I'm trying to do you can check AirBnb's app. TableView with some houses with cells and inside, pictures collectionView. Il you touch the collectionView, the tableView select the cell...
Thanks
I don't believe there is a "direct" way to do what you're asking. The collection view will respond to the gestures, so they can't "flow through" to the table view / cells without using a closure or protocol/delegate pattern.
Here's one approach...
We subclass UICollectionView and, on touchesBegan call a closure to tell the table view to select the row.
We can also implement scrollViewWillBeginDragging to select the row on collection view scroll, in addition to collection view "tap."
So...
View Controller class
class TableColTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// number of items in the collection view for each row
let myData: [Int] = [
12, 15, 8, 21, 17, 14,
16, 10, 5, 13, 20, 19,
]
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
])
tableView.register(SomeTableCell.self, forCellReuseIdentifier: "someTableCell")
tableView.dataSource = self
tableView.delegate = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "someTableCell", for: indexPath) as! SomeTableCell
cell.rowTitleLabel.text = "Row \(indexPath.row)"
cell.numItems = myData[indexPath.row]
// closure for the cell to tell us to select it
// because its collection view was tapped
cell.passThroughSelect = { [weak self] theCell in
guard let self = self,
let pth = self.tableView.indexPath(for: theCell)
else { return }
self.tableView.selectRow(at: pth, animated: true, scrollPosition: .none)
// selecting a row programmatically does NOT trigger didSelectRowAt
// so we'll call our own did select func
self.myDidSelect(pth)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// pass this on to our own did select func
// so it matches what happens when programmatically selecting the row
myDidSelect(indexPath)
}
func myDidSelect(_ indexPath: IndexPath) {
print("Table View - didSelectRowAt", indexPath)
// do something because the row was selected
}
}
Table View Cell class
class SomeTableCell: UITableViewCell {
// callback closure
var passThroughSelect: ((UITableViewCell) -> ())?
var rowTitleLabel = UILabel()
var collectionView: SubCollectionView!
var numItems: Int = 0 {
didSet {
collectionView.reloadData()
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
let fl = UICollectionViewFlowLayout()
fl.scrollDirection = .horizontal
fl.estimatedItemSize = CGSize(width: 120.0, height: 52.0)
fl.minimumLineSpacing = 12
fl.minimumInteritemSpacing = 12
collectionView = SubCollectionView(frame: .zero, collectionViewLayout: fl)
rowTitleLabel.translatesAutoresizingMaskIntoConstraints = false
collectionView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(rowTitleLabel)
contentView.addSubview(collectionView)
let g = contentView.layoutMarginsGuide
// avoid auto-layout complaints
let c = collectionView.heightAnchor.constraint(equalToConstant: 60.0)
c.priority = .required - 1
NSLayoutConstraint.activate([
rowTitleLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
rowTitleLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
rowTitleLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
rowTitleLabel.heightAnchor.constraint(equalToConstant: 30.0),
collectionView.topAnchor.constraint(equalTo: rowTitleLabel.bottomAnchor, constant: 4.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
c,
])
collectionView.register(SomeCollectionCell.self, forCellWithReuseIdentifier: "someCollectionCell")
collectionView.dataSource = self
collectionView.delegate = self
collectionView.passThroughTouch = { [weak self] in
guard let self = self else { return }
self.passThroughSelect?(self)
}
collectionView.backgroundColor = .systemYellow
}
}
extension SomeTableCell: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numItems
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "someCollectionCell", for: indexPath) as! SomeCollectionCell
cell.label.text = "Cell \(indexPath.item)"
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Collection View - didSelectItemAt", indexPath)
}
}
extension SomeTableCell: UIScrollViewDelegate {
// if you only want the table cell selected on TAP, don't implement this
// if you want the table cell selected on CollectionView SCROLL, implement this
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
self.passThroughSelect?(self)
}
}
UICollectionView subclass
class SubCollectionView: UICollectionView {
var passThroughTouch: (() -> ())?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// this allows the collection view cell to be selected
super.touchesBegan(touches, with: event)
// this tells the controller (the table view cell)
// that the collection view was tapped
passThroughTouch?()
}
}
UICollectionView Cell class
class SomeCollectionCell: UICollectionViewCell {
var label = UILabel()
var styleView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
styleView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
styleView.addSubview(label)
contentView.addSubview(styleView)
let g = contentView
NSLayoutConstraint.activate([
styleView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
styleView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
styleView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
styleView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
label.topAnchor.constraint(equalTo: styleView.topAnchor, constant: 8.0),
label.leadingAnchor.constraint(equalTo: styleView.leadingAnchor, constant: 8.0),
label.trailingAnchor.constraint(equalTo: styleView.trailingAnchor, constant: -8.0),
label.bottomAnchor.constraint(equalTo: styleView.bottomAnchor, constant: -8.0),
])
label.textColor = .white
styleView.backgroundColor = UIColor(white: 0.5, alpha: 1.0)
styleView.layer.borderColor = UIColor.white.cgColor
styleView.layer.borderWidth = 1
styleView.layer.cornerRadius = 6
}
override var isSelected: Bool {
didSet {
label.textColor = isSelected ? .red : .white
styleView.backgroundColor = isSelected ? UIColor(white: 0.95, alpha: 1.0) : UIColor(white: 0.5, alpha: 1.0)
styleView.layer.borderColor = isSelected ? UIColor.red.cgColor : UIColor.white.cgColor
}
}
}
You can do the following
First in first, print something in didSelectItem of your collectionView delegate, to make ensure that your collectionView cell is tapped.
add a delegate property in your UICollectionViewClass and call the delegate in the DidSelectItem if the step 1 is performing correctly.
In your UITableViewController, you have a function cellForRowAtIndexPath, here add the delegate property for the associate cell.
If you can print something in your delegate function, then you are at your last step. Call super.didSelect..with your indexPath, because now you have everything to call didSelect manually.
Maybe you would like to try allowsSelection property of UICollectionView. If you set this property false, your collection view cells are not selected any more.
And I think tableView didSelect can capture user interaction. If UITableView still can not capture didSelect, you can give delegate to collectionView and fire it once it's tapped by adding a tap gesture onto UICollectionView.
I have UIViewTable created programmatically
I customised the headers and cell look via Extension.
All I need is to make the large amount of texts displayed in header/cell to be viewed with:
lineBreakMode = NSLineBreakMode.byWordWrapping // enable multi line
numberOfLines = 0 // for Automatic size
I nearly used everything, but nothing is working.
I used:
self.tableView.estimatedRowHeight = 200.0
self.tableView.rowHeight = UITableView.automaticDimension
I put:
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
I also did:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
Nothing seems to work
here is my Extension:
extension indexController {
override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let subDatas = sections[section].sub_catigories // [1]
return subDatas?.count ?? 0
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let currentSection = sections[indexPath.section]
let currentSubdata = currentSection.sub_catigories?[indexPath.row]
//print(currentSubdata!.id)
let vc = indexControllerTwo()
vc.catNumber = currentSubdata!.id
vc.sectionTitle = currentSubdata?.name
navigationController?.pushViewController(vc, animated: true)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellid", for: indexPath)
// [2]
let currentSection = sections[indexPath.section]
let currentSubdata = currentSection.sub_catigories![indexPath.row]
// use listCell
guard let titleCell = cell as? listCell else {
return cell
}
titleCell.titleLabel.text = currentSubdata.name
titleCell.listCount.text = "\(currentSubdata.number_of_subcatigories ?? 0)"
// titleCell.titleLabel.numberOfLines = 3
// titleCell.titleLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
// titleCell.titleLabel.baselineAdjustment = .alignCenters
// titleCell.titleLabel.adjustsFontSizeToFitWidth = true
// self.tableView.estimatedRowHeight = 200.0
// self.tableView.rowHeight = UITableView.automaticDimension
cell.layer.backgroundColor = UIColor.clear.cgColor
return cell
}
Please note that: listCell is just for customization and constraint
and here it is:
import UIKit
class listCell: UITableViewCell {
var safeArea: UILayoutGuide!
let imageCell = UIImageView()
let titleLabel = UILabel()
let subTitleLabel = UILabel()
let listCount = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView(){
safeArea = layoutMarginsGuide
setupTitleLabel()
setupListCount()
}
func setupTitleLabel(){
addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 7)
])
titleLabel.font = UIFont(name: "verdana-Bold", size: 16)
}
func setupListCount(){
addSubview(listCount)
listCount.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
listCount.leadingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -30),
listCount.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: -11)
])
listCount.font = UIFont(name: "verdana", size: 10)
}
}
Please help me make the header and cell text field to be auto resizing.
thanks for your time.
I am looking to change the background colour and text colour of the uitableview sections using viewForHeaderInSection like so:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionHeader = UIView()
let sectionText = UILabel()
sectionHeader.backgroundColor = .blue
sectionText.textColor = .red
sectionText.font = .systemFont(ofSize: 14, weight: .bold)
sectionText.text = painkillersArray[section]["label"] as? String
sectionHeader.addSubview(sectionText)
return sectionHeader
}
The background is working but the text is not appearing. What am I doing wrong?
you need to give frame to both the view and label and also you have to provide heightForHeaderInSection:-
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionHeader = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tableView.frame.width, height: 50))
let sectionText = UILabel()
sectionText.frame = CGRect.init(x: 5, y: 5, width: sectionHeader.frame.width-10, height: sectionHeader.frame.height-10)
sectionText.text = "Custom Text"
sectionText.font = .systemFont(ofSize: 14, weight: .bold) // my custom font
sectionText.textColor = .red // my custom colour
sectionHeader.addSubview(sectionText)
return sectionHeader
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 60 // my custom height
}
===
EDIT: I removed the first code I wrote, which didn't handle autolayout correctly. Morevoer, as #rmaddy pointed out in the comment below, it would be better if you use the provided UITableViewHeaderFooterView as is.
Register
tableView.register(UITableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "header")
in viewDidLoad and do
override func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
header?.textLabel?.text = "foo"
return header
}
header?.textLabel?.textColor = .red won't work in the above method, so put customization code in
override func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
header?.textLabel?.text = "foo"
return header
}
override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
if let header = view as? UITableViewHeaderFooterView {
header.textLabel?.textColor = .red
header.backgroundView?.backgroundColor = .blue
}
}
===
To use autolayout constraints with more complex headers, provide a custom UITableViewHeaderFooterView subclass:
class CustomTableViewHeaderView: UITableViewHeaderFooterView {
func commonInit() {
let sectionHeader = UIView()
let sectionText = UILabel()
sectionHeader.backgroundColor = .blue
sectionText.textColor = .red
sectionText.font = .systemFont(ofSize: 14, weight: .bold)
sectionText.text = "foo"
sectionHeader.translatesAutoresizingMaskIntoConstraints = false
sectionText.translatesAutoresizingMaskIntoConstraints = false
sectionHeader.addSubview(sectionText)
addSubview(sectionHeader)
NSLayoutConstraint.activate([
sectionHeader.leadingAnchor.constraint(equalTo: leadingAnchor),
sectionHeader.trailingAnchor.constraint(equalTo: trailingAnchor),
sectionHeader.topAnchor.constraint(equalTo: topAnchor),
sectionHeader.bottomAnchor.constraint(equalTo: bottomAnchor),
sectionText.leadingAnchor.constraint(equalTo: sectionHeader.leadingAnchor, constant: 16),
sectionText.trailingAnchor.constraint(equalTo: sectionHeader.trailingAnchor),
sectionText.topAnchor.constraint(equalTo: sectionHeader.topAnchor, constant: 8),
sectionText.bottomAnchor.constraint(equalTo: sectionHeader.bottomAnchor, constant: -8),
])
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
}
and register it in viewDidLoad():
func viewDidLoad() {
super.viewDidLoad()
...
tableView.register(CustomTableViewHeaderView.self, forHeaderFooterViewReuseIdentifier: "header")
...
}
Then simply dequeReusableHeaderFooterView:
func tableView(_ tableView: UITableView,
viewForHeaderInSection section: Int) -> UIView? {
return tableView.dequeueReusableHeaderFooterView(withIdentifier: "header")
}
Implement heightForHeaderInSection and estimatedHeightForHeaderInSection similarly.
I'm trying to create a custom background for my tableview section. The look I'm going for is this:
How do I customize the tableview section background/layer please, rather than just the individual cells?
Edit: Just to clarify - I'm talking about the white background under 'Latest Transactions'. So the top of the section is rounded (as is bottom) but all the rows are rectangular.
Create UITableViewCell subclass and add a UIView with white color. Add left & right padding for the view to the cell. Add UILabel and other UI elements to this newly added view instead of adding in cell or its contentView. Set cell background color as UIColor.groupTableViewBackground
class CustomCell: UITableViewCell {
let bgView = UIView()
let label = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
backgroundColor = .groupTableViewBackground
bgView.backgroundColor = .white
bgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(bgView)
label.translatesAutoresizingMaskIntoConstraints = false
bgView.addSubview(label)
bgView.topAnchor.constraint(equalTo: topAnchor).isActive = true
bgView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
bgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
bgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
label.heightAnchor.constraint(equalToConstant: 30).isActive = true
label.topAnchor.constraint(equalTo: bgView.topAnchor, constant: 5).isActive = true
label.bottomAnchor.constraint(equalTo: bgView.bottomAnchor, constant: -5).isActive = true
label.leadingAnchor.constraint(equalTo: bgView.leadingAnchor, constant: 5).isActive = true
label.trailingAnchor.constraint(equalTo: bgView.trailingAnchor, constant: -5).isActive = true
}
}
Use this cell class in your tableView. And set your view controller background color and tableView background color as UIColor.groupTableViewBackground
In cellForRowAt check if the cell is first or last cell of the section. If it is the first cell of the section, apply corner radius to top left, top right corners. If the cell is the last cell of the section, apply corner radius to bottom left, bottom right corners. If the cell is in the middle remove corner radius.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.backgroundColor = .groupTableViewBackground
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
tableView.separatorInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 10)
tableView.tableFooterView = UIView()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section \(section) Title"
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as? CustomCell
?? CustomCell(style: .default, reuseIdentifier: "CustomCell")
if indexPath.row == 0 {//first cell of this section
cell.bgView.layer.cornerRadius = 15.0
cell.bgView.layer.masksToBounds = true
cell.bgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else if indexPath.row == tableView.numberOfRows(inSection: indexPath.section)-1 {//last cell of this section
cell.bgView.layer.cornerRadius = 15.0
cell.bgView.layer.masksToBounds = true
cell.bgView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
} else {
cell.bgView.layer.cornerRadius = 0
}
cell.label.text = "Row \(indexPath.row)"
return cell
}
}
Have a look at func headerView(forSection section: Int) -> UITableViewHeaderFooterView?
headerView(forSection:) - UITableView | Apple Developer
Use UITableViewDelegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
<#code#>
}
This will help you to set the view of each section header programatically.
To create a section header in tableView, use UITableViewDelegate's tableView(_:viewForHeaderInSection:) and tableView(_:heightForHeaderInSection:) methods.
func getHeaderText(for section: Int) -> String? {
switch section {
case 0:
return "Latest Transactions"
default:
return "Other Transactions"
}
//Add other cases for different sections
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: UIScreen.main.bounds.width, height: 70.0)))
headerView.backgroundColor = .clear
let label = UILabel(frame: CGRect(x: 20, y: 0, width: headerView.bounds.width - 40, height: headerView.bounds.height))
label.text = self.getHeaderText(for: section)
label.font = UIFont.systemFont(ofSize: 16.0, weight: .semibold)
headerView.addSubview(label)
return headerView
//Customize the headerView as per your requirement
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 70.0
}
In the above code, you can configure the header label in getHeaderText(for:) for each section.
Also, I've created the headerView programatically. You can use a .xib for creating a custom headerView as well. That's completely your choice.
You can customize the headerView as per your requirements either programatically or in the interface.
I need to create a table with 50 rectangular cells and:
All of the cells must have the same properties but different contents;
Each cell has to be clickable;
Cells must have different items inside of them (images, labels etc).
I started programming recently and I'm 16 so please don't bite my head off if I wasn't clear or whatever...
By the way I tried to create a table view with custom cells but I'm not sure it's working, plus I'm in landscape mode so maybe I need to create them programmatically... Can you just please tell me if I'm doing it right using the custom cells and maybe give me some advice or send me some tutorials for these things I need to do?
Don't worry. Go ahead in your achieve. Here are some examples for you.
Your ViewController
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableReuseIdentifier = "TableCell"
let tableView: UITableView! = {
let table = UITableView(frame: .zero, style: UITableViewStyle.grouped)
table.backgroundColor = .clear
table.translatesAutoresizingMaskIntoConstraints = false
table.isUserInteractionEnabled = true
table.isMultipleTouchEnabled = true
return table
}()
var items: [U]? = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
//registering your customCell into tableView
self.tableView!.register(MyCustomCell.self, forCellReuseIdentifier: tableReuseIdentifier)
}
public func setupViews() {
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items?.count ?? 0
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MyCustomCell = tableView.dequeueReusableCell(withIdentifier: tableReuseIdentifier, for: indexPath) as! MyCustomCell
//here you can manipulate some data to insert on cell
return cell
}
public func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0
}
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("When cell has been selected")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return true
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return nil
}
}
Your Custom Cell:
class MyCustomCell: UITableViewCell {
// attribute example
let lblCompanyName: UILabel = {
let label = UILabel()
label.textColor = .lightGray
label.font = UIFont.systemFont(ofSize: 17, weight: .bold)
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.6
label.numberOfLines = 2
label.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: UILayoutConstraintAxis.vertical)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
// attribute example
let lblFrequencies: UILabel = {
let label = UILabel()
label.textColor = .black
label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.setContentHuggingPriority(UILayoutPriority.defaultLow, for: UILayoutConstraintAxis.vertical)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
// attribute like imageView example
let imageLogo: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFit
image.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: UILayoutConstraintAxis.vertical)
image.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: UILayoutConstraintAxis.horizontal)
return image
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
[imageLogo, lblCompanyName, lblFrequencies].forEach { (view) in contentView.addSubview(view) }
let top: CGFloat = 20, bottom: CGFloat = -20, leading: CGFloat = 20, trailing: CGFloat = -20
self.imageLogo.topAnchor.constraint(equalTo: contentView.topAnchor, constant: top).isActive = true
self.imageLogo.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: bottom).isActive = true
self.imageLogo.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: leading).isActive = true
self.imageLogo.widthAnchor.constraint(equalTo: imageLogo.heightAnchor).isActive = true
self.lblCompanyName.topAnchor.constraint(equalTo: contentView.topAnchor, constant: top).isActive = true
self.lblCompanyName.leadingAnchor.constraint(equalTo: self.imageLogo.trailingAnchor, constant: 20).isActive = true
self.lblCompanyName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: trailing).isActive = true
self.lblFrequencies.topAnchor.constraint(equalTo: self.lblCompanyName.bottomAnchor, constant: 5).isActive = true
self.lblFrequencies.leadingAnchor.constraint(equalTo: self.imageLogo.trailingAnchor, constant: 20).isActive = true
self.lblFrequencies.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: trailing).isActive = true
}
}
Hope it helped you. Cheers.