I Want to add distance between cells(Top and bottom) and distance between cells and table view's walls(left, right): I need to do it using by UIEdgeInset, not by adding headerView to cell:
// Inside UITableViewCell subclass
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
}
Swift 4.2
override func layoutSubviews() {
super.layoutSubviews()
//set the values for top,left,bottom,right margins
let margins = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
contentView.frame = contentView.frame.inset(by: margins)
}
In cellforRowat populate data according to section like indexPath.section. and add below method
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView: UIView = UIView(frame: CGRect(x: 0, y: 0, width: self.myTableView.frame.width, height: 10))
headerView.backgroundColor = UIColor.clear
return headerView
}
Related
Hey I have a tableview controller and I want to create dynamic cell height with label and ImageViews. I tried something like below but it doesn't work for me. Where is the mistake about code. Any idea ?
class NotificationsPageController: UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(NotificationCell.self, forCellReuseIdentifier: notCell)
view.backgroundColor = .black
tableView.estimatedRowHeight = 60
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
class NotificationCell: UITableViewCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor(red: 18/255, green: 18/255, blue: 18/255, alpha: 1)
setNotificationAnchors()
}
fileprivate func setNotificationAnchors() {
addSubview(notiType)
notiType.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 6, paddingBottom: 0, paddingRight: 20, width: 0, height: 0)
//notiType.centerYAnchor.constraint(equalTo: notiImage.centerYAnchor).isActive = true
addSubview(notiImage)
notiImage.anchor(top: nil, left: nil, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 0, height: 0)
notiImage.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
First, I strongly recommend you learn how constraints and auto-layout work. The fact that the code you posted shows you are using a .anchor(top:left:bottom:right:...) type of "constraint helper" indicates that you don't understand it yet.
So, here is a simple example. Note that this is not a complete implementation, but it should get you headed in the right direction:
class NotificationCell: UITableViewCell {
let notiType: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.numberOfLines = 0
return v
}()
let notiImage: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = UIColor(red: 18/255, green: 18/255, blue: 18/255, alpha: 1)
setNotificationAnchors()
}
fileprivate func setNotificationAnchors() {
contentView.addSubview(notiType)
contentView.addSubview(notiImage)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
// constrain label Top, Leading, Bottom to contentView margins
notiType.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
notiType.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
notiType.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
// constrain image view Right and centerY to contentView margins
notiImage.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
notiImage.centerYAnchor.constraint(equalTo: g.centerYAnchor, constant: 0.0),
// guessing square (1:1 ratio) image view, 24 x 24
notiImage.widthAnchor.constraint(equalToConstant: 24.0),
notiImage.heightAnchor.constraint(equalTo: notiImage.widthAnchor),
// leave 8-pts space between label and image view
notiType.trailingAnchor.constraint(equalTo: notiImage.leadingAnchor, constant: -8.0),
])
// during development, so we can easily see the element frames
notiType.backgroundColor = .cyan
notiImage.backgroundColor = .red
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ExampleTableViewController: UITableViewController {
let notCell = "notCell"
let theData: [String] = [
"Single line label.",
"This lable will have enough text that it will need to wrap onto multiple lines.",
"Another single line cell.",
"Here is a description of a table view cell: Defines the attributes and behavior of cells in a table view. You can set a table cell's selected-state appearance, support editing functionality, display accessory views (such as a switch control), and specify background appearance and content indentation.",
"This is the fifth row.",
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(NotificationCell.self, forCellReuseIdentifier: notCell)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: notCell, for: indexPath) as! NotificationCell
cell.notiType.text = theData[indexPath.row]
// set image here...
//cell.notiImage.image = ...
return cell
}
}
Result - Multi-line label (cyan background) and 24x24 image (red background):
By the way... Step-by-step tutorials on this can be found in many, many places - a little tough to think you searched and couldn't find anything like this.
Follow the steps below:
Add the below lines in ViewDidLoad
tableView.dataSource = self
tableView.delegate = self
// Set automatic dimensions for row height
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = UITableViewAutomaticDimension
In XIB or Cell if you have label update the numberOLines = 0 and make the lineBreakMode = truncateTail
Update constraints to top, bottom, right and left to its superView
so I currently have a TableViewController that has headerview and prototype cells. Inside the headerview, there is an image. I have programmed it to where a user scrolls down, the headerview stretches down. But I want to find a way to make that header view stick where it is at and when a user scrolls up on the table view, the cells go over the headerview instead of the headerview scrolling up with the cells which it currently is doing. Here is my code for the TableViewController:
import UIKit
class HeaderViewTableViewController: UITableViewController {
var image: UIImageView?
var image2: UIImageView?
var songNameArray = ["Intro", "The Box", "Start wit Me (feat. Gunna)", "Perfect Time", "Moonwalkin (feat. Lil Durk)", "Big Stepper", "Gods Eyes", "Peta (feat. Meek Mill)", "Boom Boom Boom", "Elyse's Skit", "High Fashion (feat. Mustard)", "Bacc Seat (feat. Ty Dolla $ign)", "Roll Dice", "Prayers to the Trap God", "Tip Toe (feat. A Boogie wi da Hoodie)", "War Baby"]
private let tableHeaderViewHeight: CGFloat = 350.0
private let tableHeaderViewCutAway: CGFloat = 0.1
var headerView: HeaderView!
var headerMaskLayer: CAShapeLayer!
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView()
headerView = tableView.tableHeaderView as! HeaderView
headerView.imageView = image
tableView.tableHeaderView = nil
tableView.addSubview(headerView)
tableView.contentInset = UIEdgeInsets(top: tableHeaderViewHeight, left: 0, bottom: 0, right: 0)
tableView.contentOffset = CGPoint(x: 0, y: -tableHeaderViewHeight + 64)
//cut away header view
headerMaskLayer = CAShapeLayer()
headerMaskLayer.fillColor = UIColor.black.cgColor
headerView.layer.mask = headerMaskLayer
let effectiveHeight = tableHeaderViewHeight - tableHeaderViewCutAway/2
tableView.contentInset = UIEdgeInsets(top: effectiveHeight, left: 0, bottom: 0, right: 0)
tableView.contentOffset = CGPoint(x: 0, y: -effectiveHeight)
updateHeaderView()
}
func updateHeaderView() {
let effectiveHeight = tableHeaderViewHeight - tableHeaderViewCutAway/2
var headerRect = CGRect(x: 0, y: -effectiveHeight, width: tableView.bounds.width, height: tableHeaderViewHeight)
if tableView.contentOffset.y < -effectiveHeight {
headerRect.origin.y = tableView.contentOffset.y
headerRect.size.height = -tableView.contentOffset.y + tableHeaderViewCutAway/2
}
headerView.frame = headerRect
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y:0))
path.addLine(to: CGPoint(x: headerRect.width, y: 0))
path.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height))
path.addLine(to: CGPoint(x: 0, y: headerRect.height - tableHeaderViewCutAway))
headerMaskLayer?.path = path.cgPath
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.tableView.decelerationRate = UIScrollView.DecelerationRate.fast
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
}
#IBAction func backButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
extension HeaderViewTableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return songNameArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SongCell", for: indexPath) as! SongNameTableViewCell
cell.songName.text = songNameArray[indexPath.row]
return cell
}
}
extension HeaderViewTableViewController {
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
updateHeaderView()
}
}
Any idea on how to achieve this? Also I have a button nested inside the headerview that I would like to stick to the top of the view controller when users scroll up. I have it nested there because I'm not sure where else it could go in a table view like a custom cell.
Heres an image of how it is setup in my storyboard and what it looks like when ran on the simulator.
Code
import UIKit
class ViewController:
UIViewController,UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var suma=UITableViewCell()
var sampleviewpurple=UIView(frame: CGRect(x: suma.frame.width/0, y: 0, width: suma.frame.width/2, height: suma.frame.height))
sampleviewpurple.backgroundColor=#colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)
var sampleviewred=UIView(frame: CGRect(x: suma.frame.width/1, y: 0, width: suma.frame.width/2, height: suma.frame.height))
sampleviewred.backgroundColor=#colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
suma.contentView.addSubview(sampleviewpurple)
suma.contentView.addSubview(sampleviewred)
return suma
}
#IBOutlet weak var tab: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tab.dataSource=self
tab.delegate=self
}
}
when I try to add a subview to the suma and return to the delegate, My table view is still blank, I am returning valid data source to the data source when and also noticed the reference to the table view all is fine, But still how I can not able to add a subview to the uitableviewcell
In the cellForRowAt function, you must dequeue your table cell, not instantiate UITableViewCell:
var suma = tableView.dequeueReusableCell(withIdentifier: "YOUR_CELL_IDENTIFIER")
For setting your view's frame you are using the cell's frame which is not set yet. You should use such a view which frame is already set and on which bases you want to calculate the frame of your new views.
Try this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var sumaCell:UITableViewCell?
sumaCell = tableView.dequeueReusableCell(withIdentifier: "sumaCell")
if sumaCell == nil {
sumaCell = UITableViewCell(style: .default, reuseIdentifier: "sumaCell")
}
var sampleviewpurple=UIView(frame: CGRect(x: 0, y: 0, width: ((sumaCell?.frame.width)!/2), height: (sumaCell?.frame.height)!))
sampleviewpurple.backgroundColor=#colorLiteral(red: 0.2196078449, green: 0.007843137719, blue: 0.8549019694, alpha: 1)
var sampleviewred=UIView(frame: CGRect(x: (sumaCell?.frame.width)!/2, y: 0, width: ((sumaCell?.frame.width)!/2), height: (sumaCell?.frame.height)!))
sampleviewred.backgroundColor=#colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
sumaCell?.contentView.addSubview(sampleviewpurple)
sumaCell?.contentView.addSubview(sampleviewred)
return sumaCell!
}
You are creating the cell in code. First of all, you have to register the UITableViewCell in viewDidLoad method.
tab.register(UITableViewCell.self, forCellReuseIdentifier: "DefaultCell")
Then you have to use the dequeue cell in cellForRowAt method.
var suma = tab.dequeueReusableCell(withIdentifier: "DefaultCell")
If you are instantiating the cell from storyboard, you just have to use dequeue cell. No need to register.
I made three change.
Dequeue the cell for reusability.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var suma = UITableViewCell()
suma = tab.dequeueReusableCell(withIdentifier: "DefaultCell")!`
Changed the sampleviewpurple and sampleviewred x position
let sampleviewpurple = UIView(frame: CGRect(x: 0, y: 0, width: suma.frame.width/2, height: suma.frame.height))
let sampleviewred = UIView(frame: CGRect(x: suma.frame.width/2, y: 0, width: suma.frame.width/2, height: suma.frame.height))
Registered the cell which is created programmatically to create a connection between UITableView and UITableViewCell in viewDidLoad function
tab.register(UITableViewCell.self, forCellReuseIdentifier: "DefaultCell")
I want to make my tableview header have automatic height because I have label in my header view that can have multi line text.
I've set following steps:
in view did load ad
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 200
}
But it still not working.
Here is my storyboard configuration (I have set lines to 0 and line break to word wrap.
Another question: Is the custom view I added on the top of TableViewCell is a header cell?
I've ended up using method to calculate height for label and added the height to the View.
private func heightForView(text: String, font: UIFont, width: CGFloat) -> CGFloat {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}
In the viewDidLoad()
let labelHeight = heightForView(text: post.content, font: contentLabel.font, width: contentLabel.frame.width)
headerView.frame.size.height = headerView.frame.size.height + labelHeight
Still curious why the UITableViewAutomaticDimension not working in this case.
I have a table view embedded in an iCarousel item and firstly I can't seem to set the view frame whether it's in viewDidLayoutSubviews() but also when I tap the Tableview all the cells disappear. I have created the Tableview in its own ViewController as each carousel item will need editing buttons for the Tableviews they contain. If I don't put the Tableview in its own view controller the cells remain but I get an index out of range when scrolling a lot of views and I can't add editing buttons. I've run out of ideas and would love some pointers or tips.
class BillSplitterTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
var splitter: BillSplitter?
var tableView: UITableView!
var viewFrame: CGRect?
init(frame: CGRect, splitter: BillSplitter) {
super.init(nibName: nil, bundle: nil)
self.splitter = splitter
self.viewFrame = frame
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:)")
}
override func viewDidLoad() {
super.viewDidLoad()
let subView = UIView(frame: viewFrame!)
view.backgroundColor = .clear
tableView = UITableView(frame: view.frame)
tableView.delegate = self
tableView.dataSource = self
tableView.frame = subView.frame
tableView.separatorStyle = .none
tableView.estimatedRowHeight = 35
let tableViewBackground = UIImageView(image: UIImage(data: splitter?.image as! Data, scale:1.0))
tableViewBackground.contentMode = .scaleAspectFit
tableViewBackground.frame = tableView.frame
tableView.backgroundView = tableViewBackground
subView.addSubview(tableView)
view.addSubview(subView)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (splitter?.items?.count)! > 0 {
return (splitter!.items?.count)!
} else {
TableViewHelper.EmptyMessage("\(splitter?.name!) has no items to pay for.", tableView: tableView)
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.register(SplitterCarouselItemTableViewCell.classForCoder(), forCellReuseIdentifier: "splitterCarouselItemTableViewCell")
let cell: SplitterCarouselItemTableViewCell = tableView.dequeueReusableCell(withIdentifier: "splitterCarouselItemTableViewCell") as! SplitterCarouselItemTableViewCell
let item = (splitter?.items?.allObjects as! [Item])[indexPath.row]
let count = item.billSplitters?.count
cell.backgroundColor = UIColor(netHex: 0xe9edef).withAlphaComponent(0.5)
if count! > 1 {
cell.name!.text = "\(item.name!)\nsplit \(count!) ways"
cell.price!.text = "£\(Double(item.price)/Double(count!))"
} else {
cell.name!.text = item.name!
cell.price!.text = "£\(item.price)"
}
return cell
}
}
the carousel viewcontroller:
func numberOfItems(in carousel: iCarousel) -> Int {
return allBillSplitters.count
}
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
let splitter = allBillSplitters[index]
carouselIndex = index
let splitterView = SplitterCarouselItemView(frame: CGRect(x: 0, y: 0, width: width, height: height))
let viewWidth = Int(splitterView.frame.width)
let nameLabel = SplitterCarouselItemNameLabel(frame: CGRect(x: 0, y: 0, width: viewWidth, height: 30))
let emailLabel = SplitterCarouselItemEmailLabel(frame: CGRect(x: 0, y: 30, width: viewWidth, height: 20))
let addItemButton = SplitterCarouselItemButton(frame: CGRect(x: viewWidth - 45, y: 50, width: 35, height: 35), addsItem: true)
let deleteItemButton = SplitterCarouselItemButton(frame: CGRect(x: 15, y: 50, width: 35, height: 35), addsItem: false)
let itemHeight = Int(splitterView.frame.height)
let payTotalButton = SplitterCarouselItemPayButton(frame: CGRect(x: 0, y: itemHeight - 35, width: viewWidth + 1, height: 35))
let tableViewHeight = Int(height - 130)
let frame = CGRect(x: 0, y: 85, width: viewWidth, height: tableViewHeight)
let tableView = BillSplitterTableView(frame: frame, splitter: splitter)
splitterView.addSubview(nameLabel)
splitterView.addSubview(emailLabel)
splitterView.addSubview(addItemButton)
splitterView.addSubview(deleteItemButton)
splitterView.addSubview(tableView.view)
splitterView.addSubview(payTotalButton)
nameLabel.text = "\(allBillSplitters[index].name!)"
emailLabel.text = "\(allBillSplitters[index].email!)"
payTotalButton.setTitle("Pay £\(allBillSplitters[index].total)", for: .normal)
return splitterView
}
func carousel(_ carousel: iCarousel, valueFor option: iCarouselOption, withDefault value: CGFloat) -> CGFloat {
if allBillSplitters.count > 2 {
switch option {
case .spacing:
return value * 1.05
case .fadeMin:
return 0.0
case .fadeMinAlpha:
return 0.3
case .fadeMax:
return 0.0
default:
return value
}
}
return value
}
Thanks for any help in advance