i am trying to make space between cells in UItableview i've checked google all posts i found is more than 3 years old , when i try to apply them i am getting so many errors, is it possible to make space between cells in UItableview ?
my Code :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as! ImageCell
cell.model = self.modelAtIndexPath(indexPath)
let url = URL(string: cell.model.imageName)
cell.imgBack.kf.setImage(with: url)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//send back playlist count
if (section == 0) {
return self.lists_arr.count
}
return 0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated:true)
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let imageCell = cell as! ImageCell
self.setCellImageOffset(imageCell, indexPath: indexPath)
}
//number of sections in table
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func modelAtIndexPath(_ indexPath: IndexPath) -> CellPlaylist {
return self.lists_arr[(indexPath as NSIndexPath).row % self.lists_arr.count]
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView == self.tblMain) {
for indexPath in self.tblMain.indexPathsForVisibleRows! {
if let imgCel : ImageCell = self.tblMain.cellForRow(at: indexPath) as? ImageCell {
self.setCellImageOffset(imgCel, indexPath: indexPath)
}
}
}
}
func setCellImageOffset(_ cell: ImageCell, indexPath: IndexPath) {
let cellFrame = self.tblMain.rectForRow(at: indexPath)
let cellFrameInTable = self.tblMain.convert(cellFrame, to:self.tblMain.superview)
let cellOffset = cellFrameInTable.origin.y + cellFrameInTable.size.height
let tableHeight = self.tblMain.bounds.size.height + cellFrameInTable.size.height
let cellOffsetFactor = cellOffset / tableHeight
cell.setBackgroundOffset(cellOffsetFactor)
}
The class of TableCell :
class ImageCell: UITableViewCell {
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var imgBack: UIImageView!
#IBOutlet weak var imgBackTopConstraint: NSLayoutConstraint!
#IBOutlet weak var imgBackBottomConstraint: NSLayoutConstraint!
let imageParallaxFactor: CGFloat = 70
var imgBackTopInitial: CGFloat!
var imgBackBottomInitial: CGFloat!
var model: CellPlaylist! {
didSet {
self.updateView()
}
}
override func awakeFromNib() {
self.clipsToBounds = true
self.imgBackBottomConstraint.constant -= 2 * imageParallaxFactor
self.imgBackTopInitial = self.imgBackTopConstraint.constant
self.imgBackBottomInitial = self.imgBackBottomConstraint.constant
}
func updateView() {
// self.imgBack.imageFromServerURL(urlString: self.model.imageName)
//self.getImage(url: self.model.imageName,imgView: self.imgBack)
//self.model.imageName
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
self.layer.cornerRadius = 8
self.layer.shadowOffset = CGSize(width: 0, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowOpacity = 0.3
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 8, height: 8)).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
self.contentView.layoutMargins.top = 20
self.lblTitle.text = self.model.title
}
func setBackgroundOffset(_ offset:CGFloat) {
let boundOffset = max(0, min(1, offset))
let pixelOffset = (1-boundOffset)*2*imageParallaxFactor
self.imgBackTopConstraint.constant = self.imgBackTopInitial - pixelOffset
self.imgBackBottomConstraint.constant = self.imgBackBottomInitial + pixelOffset
}
}
The storyboard :
what i am getting
:
what i want :
Make Tablview BG colour white and set BG View colour in cell that you want to keep see in image i have set bgview BG colour is light greay and make BGview hight smaller then cell i mean if you want to set space 10 pixel keep Bg hight smaller then cell 10 pixel
Here is my cell
And here is out put
Update: UIEdgeInsetsInsetRect method is replaced now you can do it like this
contentView.frame = contentView.frame.inset(by: margins)
Swift 4 answer:
in your custom cell class add this function
override func layoutSubviews() {
super.layoutSubviews()
//set the values for top,left,bottom,right margins
let margins = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
contentView.frame = UIEdgeInsetsInsetRect(contentView.frame, margins)
}
You can change values as per your need
Swift 4.2 solution
Inside UITableViewCell subclass
override func layoutSubviews() {
super.layoutSubviews()
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
bounds = bounds.inset(by: padding)
}
Swift 5.0 solution
Inside UITableViewCell subclass
override func layoutSubviews() {
super.layoutSubviews()
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
bounds = UIEdgeInsetsInsetRect(bounds, padding)
}
You can let every cell to take a section in group tableView, then you set the section's footer.
Try my code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.register(UINib.init(nibName: "VC1Cell", bundle: nil), forCellReuseIdentifier: "VC1Cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: -- tableview delegate
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view:UIView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.bounds.size.width, height: 10))
view.backgroundColor = .clear
return view
}
func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:VC1Cell = tableView.dequeueReusableCell(withIdentifier: "VC1Cell") as! VC1Cell
return cell
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 10.0
}
}
The result:
Building on Gulfam Khan's answer, the new way of setting the inset:
Swift 4:
override func layoutSubviews() {
super.layoutSubviews()
//set the values for top,left,bottom,right margins
let margins = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
contentView.frame = contentView.frame.inset(by: margins)
}
Swift 4:
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0))
}
Swift 5.2 Solution
override func layoutSubviews() {
super.layoutSubviews()
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
bounds = bounds.inset(by: padding)
}
Related
In my application I used parallax effect on tableview. So I used with UIEdgeInsets to show effect. It is perfect work without section but when I tried to with section it look ugly and section get zerk while scroll up and down. I have attached clip for more clearance. And I using bellow code. link Please help me
override func viewDidLoad()
{
super.viewDidLoad()
tblItemList.contentInset = UIEdgeInsets(top: 200, left: 0, bottom: 0, right: 0)
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 200)
imageView.image = UIImage.init(named: "32621859132_25f783b550_o.jpg")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
//imageView.backgroundColor = .white
view.addSubview(imageView)
}
extension RestaurantDetailsVC : UITableViewDataSource, UITableViewDelegate
{
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 105
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemSectionCell") as! ItemSectionCell
cell.backgroundColor = .white
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
//Scrollview delegate
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let y = 200 - (scrollView.contentOffset.y + 200)
let height = min(max(y, 0), 300)
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: height)
if height <= 0
{
UIView.animate(withDuration: Double(0.1), animations: {
self.constraintTopForViewBar.constant = 0
self.viewForTopBar.isHidden = false
self.view.layoutIfNeeded()
}) { (result) in}
self.tblItemList.contentInset = UIEdgeInsets(top: 68, left: 0, bottom: 0, right: 0)
}
else
{
UIView.animate(withDuration: Double(0.1), animations: {
self.constraintTopForViewBar.constant = -68
self.viewForTopBar.isHidden = true
self.view.layoutIfNeeded()
}) { (result) in}
self.tblItemList.contentInset = UIEdgeInsets(top: 200, left: 0, bottom: 0, right: 0)
}
//print(height)
}
}
I have a TableViewController class :-
class SideMenuViewController: UITableViewController {
let tableViewFooter = UIView(frame: CGRect(x: 0, y: self.view.bounds.size.height + 80, width: self.view.bounds.width, height: 80))
tableViewFooter.backgroundColor = UIColor(patternImage: UIImage(named: "nav_drawer_footer.jpg")!)
tableView.tableFooterView = tableViewFooter
}
But using this code ..the view is under the last cell of tableView....i don't want this.
What I need is an ImageView / UIView at the Bottom of the table view Controller
let theHeight = view.frame.size.height //grabs the height of your view
let footer = UIView()
footer.backgroundColor = UIColor(patternImage: UIImage(named: "nav_drawer_footer.jpg")!)
footer.frame = CGRect(x: 0, y: theHeight - 150 , width: self.view.frame.width, height: 80)
self.view.addSubview(footer)
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView = UIView()
let imgView = UIImageView()
//Write UIImageView required code
footer.addSubView(imgView)
return footerView
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return HEIGHT_YOU_WANT
}
I would do this with UIViewController with separate UITableView and UIImageView instead of using UITableViewController.When using UITableViewController self.view is actually self.tableView, i.e. your image will be a subview of the UITableView and it will overlap the bottom rows. It is also better to use layout constraints than frame rects. As constraints code is somewhat a pain to write you can use SnapKit as I did here:
import SnapKit
class SideMenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView()
let tableViewFooter = UIView()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.delegate = self
tableView.dataSource = self
tableViewFooter.backgroundColor = UIColor(patternImage: UIImage(named: "nav_drawer_footer.jpg")!)
view.addSubview(tableView)
view.addSubview(tableViewFooter)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableViewFooter.snp.makeConstraints { (make) -> Void in
make.width.equalTo(view.snp.width)
make.left.equalTo(view.snp.left)
make.bottom.equalTo(view.snp.bottom)
make.height.equalTo(80)
}
tableView.snp.makeConstraints { (make) in
make.top.equalTo(view.snp.top)
make.bottom.equalTo(tableViewFooter.snp.top)
make.width.equalTo(view.snp.width)
make.centerX.equalTo(view.snp.centerX)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.backgroundColor = UIColor.blue
return cell
}
}
Edit: I am using SnapKit just so I don't write all this constraints code.The point is in the logic.
I am implementing a swipe to delete feature on all UITableViewCell in my UITableView. In the same time I would like use a custom button, and for this purpose, this tutorial came in handy: Custom edit view in UITableViewCell while swipe left. Objective-C or Swift
(I went with the 4th solution). So far, I am satisfied with my overall result as it work out just well. However, I have noticed that in my case, when swiping on the cell, the cell travel does not stop just after the hidden button is unveiled, rather is continues and it unveils some gray space. Once the cell is released, it bounces back, covering the gray space. The big question is, how do I disable this bounce effect and have the cell stop on slide just after the button is unveiled?
My problem is very similar to this one How to remove UITableViewCell swipe to delete bounce
. However, I've tried all UISwipeViewDelegate solutions and none of them work for me.
What I need it to be like:
What it's actually like:
This is my code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
lazy var rowHeight: CGFloat = {
return 82
}()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let nibName = UINib(nibName: "TableViewCell", bundle: nil)
tableView.register(nibName, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSections(in tableView: UITableView) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.makeCellWith(image: "sampleImage", userName: "userNameSample")
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return rowHeight
}
// MARK: - Cell Sections
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.frame = UIEdgeInsetsInsetRect(view.frame, UIEdgeInsetsMake(0, 10, 0, 10))
}
// Set the spacing between sections
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
// Make the background color show through
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clear
return headerView
}
// MARK: - Slide to delete feature
fileprivate func whitespaceString(font: UIFont = UIFont.systemFont(ofSize: 15), width: CGFloat) -> String {
let padding: CGFloat = 20
let mutable = NSMutableString(string: "")
let attribute = [NSAttributedStringKey.font: font]
while mutable.size(withAttributes: attribute).width < width - (2 * padding) {
mutable.append(" ")
}
return mutable as String
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let whitespace = whitespaceString(width: rowHeight)
let deleteAction = UITableViewRowAction(style: .default, title: whitespace) { (action, indexPath) in
// do action on delete here
}
// create a color from pattern image and set the color as a background color of action
let view = UIView(frame: CGRect(x: 0, y: 0, width: rowHeight, height: rowHeight))
view.backgroundColor = UIColor.white
let imageSize: CGFloat = 50
let imageView: UIImageView = {
let iv = UIImageView()
let buttonImage = UIImage(named: "plusButton")
iv.image = buttonImage
return iv
}()
imageView.frame = CGRect(x: (view.frame.height - imageSize)/2,
y: (view.frame.width - imageSize)/2,
width: imageSize,
height: imageSize)
view.addSubview(imageView)
let image = view.image()
deleteAction.backgroundColor = UIColor(patternImage: image)
return [deleteAction]
}
// MARK: - Delete Object
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
fileprivate extension UIView {
func image() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
guard let context = UIGraphicsGetCurrentContext() else {
return UIImage()
}
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
I'm using code below to get rid of last static cell bottom separator line and it works perfectly for cases when I don't need to reload tableView. Once I reload my tableView separator line appears.
Only code in super VC:
override func viewDidLoad() {
super.viewDidLoad()
let footerFrame = CGRect(x: 0.0, y: 0.0, width:
tableView.frame.size.width, height: 1.0)
tableView.tableFooterView = UIView(frame: footerFrame)
}
And after calling:
fileprivate func showDatePicker()
{
datePicker.setValue(UIColor.white, forKey: "textColor")
showsDatePicker = true
tableView.beginUpdates()
tableView.endUpdates()
tableView.contentInset = UIEdgeInsets(top: -20.0, left: 0.0,
bottom: 0.0, right: 0.0)
}
in my child's tableView(didSelectRowAt indexPath:_) I'm just changing the cell's height with date picker in:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard showsDatePicker else {
switch indexPath.row {
case CellIndex.withDatePicker:
return 0.0
default:
return 44.0
}
}
switch indexPath.row {
case CellIndex.withDatePicker:
return 217.0
default:
return 44.0
}
}
and after that I have separator line drawn! Maybe it's because of STATIC cells usage and the only way is to add blank views to each viewController in storyboard? Replacing tableView.tableFooterView = UIView() to viewDid/WillLayoutSubviews causes empty screen effect. I don't want to add a blank view to all my controllers in the storyboards, maybe there is a way to do it programmatically?
Before displaying your last cell.
if indexPath.row == Your_last_Cell {
cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0);
}
create footer with minimum height:
func tableView(_ tableView: UITableView,
heightForFooterInSection section: Int) -> CGFloat{
// This will create a "invisible" footer
return 0.01f; //CGFLOAT_MIN
}
OR
func tableView(_ tableView: UITableView,
viewForFooterInSection section: Int) -> UIView?{
UIView(frame: CGRectZero)
}
My approach to attain this result is as follows,
In your viewDidLoad function,
override func viewDidLoad() {
super.viewDidLoad()
// Add this line
yourTableView.tableFooterView = UIView()
}
Define the number of sections for the table view
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
And in your cellForRowAtIndexPath Function,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell?
switch indexPath.section {
case 0:
cell = tableView.dequeueReusableCellWithIdentifier("yourfirstcell")
if indexPath.row == yourLastRowIndex {
self.separatorInset = UIEdgeInsetsMake(0, self.bounds.size.width, 0, 0)
}
case 1:
cell = tableView.dequeueReusableCellWithIdentifier("yoursecondcell")
default:
break
}
return cell!
}
This approach will help you.
I'm having a problem with my parallax effect. I'm having a tableView and a ImageView above the tableView. Now when the user scrolls from the top I want to stretch the image a bit. But the problem is that my tableView keeps having a white background like you can see on the screenshot. So the image isn't visible. The screenshot is taken when the viewcontroller loads and then I just pull down as far as I can go. The tableView has backgroundColor .clear so I don't why it isn't working.
My code:
import UIKit
import PureLayout
class ViewController: UIViewController {
lazy var headerImageView: UIImageView = {
let imageView = UIImageView(forAutoLayout: ())
imageView.image = UIImage(named: "test")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
lazy var tableView: UITableView = {
let tableView = UITableView(forAutoLayout: ())
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.isOpaque = false
tableView.backgroundColor = .clear
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "default")
tableView.tableFooterView = UIView()
tableView.showsVerticalScrollIndicator = false
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.headerImageView)
self.headerImageView.autoPinEdge(toSuperviewEdge: .left)
self.headerImageView.autoPinEdge(toSuperviewEdge: .right)
self.headerImageView.autoPin(toTopLayoutGuideOf: self, withInset: 0)
self.headerImageView.autoSetDimension(.height, toSize: 100)
self.view.addSubview(self.tableView)
self.tableView.autoPinEdge(toSuperviewEdge: .left)
self.tableView.autoPinEdge(toSuperviewEdge: .right)
self.tableView.autoPinEdge(toSuperviewEdge: .bottom)
self.tableView.autoPinEdge(.top, to: .bottom, of: self.headerImageView)
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "default", for: indexPath)
cell.textLabel?.text = "test"
cell.backgroundColor = .red
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Parallax functionality
let yOffset = scrollView.contentOffset.y * 0.2
let availableOffset = min(yOffset, 60)
let contentRectYOffset = availableOffset / self.headerImageView.frame.size.height
self.headerImageView.layer.contentsRect = CGRect(x: 0.0, y: contentRectYOffset, width: 1, height: 1)
}
}