I have created the UITableView with the custom UITableViewCell. But the problem which I am getting is the width of the cells is not the frame width though I have assigned in the CGReact. Please have a look over my code :
CustomTableViewCell Class:
import UIKit
class CustomTableViewCell: UITableViewCell {
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width, height: 76))
view.backgroundColor = .red
view.layer.applySketchShadow()
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
lazy var iconTime : UIImageView = {
var object = UIImageView(frame: CGRect(x: 10, y: 54, width: 12, height: 12))
object.image = #imageLiteral(resourceName: "clock")
return object
}()
lazy var notification : UILabel = {
var object = UILabel(frame: CGRect(x: 10, y: 7, width: backView.frame.width, height: 40))
object.adjustsFontSizeToFitWidth = true
object.minimumScaleFactor = 0.5
object.font = object.font.withSize(28.0)
object.numberOfLines = 3
return object
}()
lazy var notificationTime : UILabel = {
var object = UILabel(frame: CGRect(x: 30, y: 40, width: backView.frame.width, height: 40))
object.adjustsFontSizeToFitWidth = true
object.minimumScaleFactor = 0.5
object.font = object.font.withSize(12.0)
return object
}()
override func layoutSubviews() {
contentView.backgroundColor = UIColor.clear
backgroundColor = UIColor.clear
backView.layer.cornerRadius = 5
backView.clipsToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
addSubview(backView)
[notification, notificationTime, iconTime].forEach(backView.addSubview(_:))
}
}
And my view controller as follows :
import UIKit
class UserModal {
var tableView = UITableView()
var notification: String?
var notificationTime : String?
init(notification: String, notificationTime: String) {
self.notification = notification
self.notificationTime = notificationTime
}
}
class newNotificationController : UIViewController {
var tableView = UITableView()
var userMod = [UserModal]()
override func viewDidLoad() {
super.viewDidLoad()
setTableView()
userMod.append(UserModal(notification: "Data ", notificationTime: "Time"))
userMod.append(UserModal(notification: "This is some Notification which needs to be populated in the Grid view for testing but lets see what is happening here!! ", notificationTime: "12-12-1212 12:12:12"))
userMod.append(UserModal(notification: "Data ", notificationTime: "Time"))
}
func setTableView() {
tableView.frame = self.view.frame
tableView.backgroundColor = UIColor.clear
tableView.delegate = self
tableView.dataSource = self
tableView.separatorColor = UIColor.clear
self.view.addSubview(tableView)
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
}
}
extension newNotificationController: UITableViewDelegate , UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userMod.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? CustomTableViewCell else { fatalError("Unable to populate Notification History")}
cell.notification.text = userMod[indexPath.row].notification
cell.notificationTime.text = userMod[indexPath.row].notificationTime
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 85
}
}
Please have a look over the result:
I am not getting it why the width of my cells is the width of the frame. Any help will be highly appreciated. Thanks!!
The problem is in this code is frame width, somehow the width of the self is not the width of a device, so because of this, you are facing this issue.
lazy var backView : UIView = {
let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width, height: 76))
view.backgroundColor = .red
view.layer.applySketchShadow()
return view
}()
To resolve this issue you can set frame like this
let view = UIView(frame: CGRect(x: 10, y: 6, width: UIScreen.main.bounds.size.width - 10, height: 76))
You set the width your view
UIView(frame: CGRect(x: 5, y: 6, width: self.frame.width - 10,
height: 76))
tableView.frame = CGRect(x: 0, y: 0, width:
self.view.frame.size.width, height: self.view.frame.size.height)
you need to give constraint to the tableview. Top, Leading, Trailing, Bottom.
put this tableView.translatesAutoresizingMaskIntoConstraints = false line in your function
func setTableView() {
tableView.frame = self.view.frame
tableView.backgroundColor = UIColor.clear
tableView.delegate = self
tableView.dataSource = self
tableView.separatorColor = UIColor.clear
self.view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false //add this line
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
}
and change width: UIScreen.main.bounds.size.width - 10 it.
thanks..
Related
I have a UITableView; without a tableHeaderView tapping a row and triggering didSelectRowAt returns the correct index path.
When I set the tableHeaderView property, didSelectRowAt either does not fire or returns the tappedRow + 2. Where am I going wrong?
Here is my code
class MenuController: UIViewController {
// Mark -- Properties
var tableView: UITableView!
var delegate: HomeControllerDelegate?
var headerView: HeaderView? = nil
var user: User? = nil
// Mark -- Init
override func viewDidLoad() {
super.viewDidLoad()
configureTableView()
if let user = self.user {
populateMenuHeader(email: user.email, firstName: user.firstName, lastName: user.lastName, imageUrl: user.imageUrl)
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
//updateHeaderViewHeight(for: tableView.tableHeaderView)
}
func updateHeaderViewHeight(for header: UIView?) {
guard let headerView = headerView else { return }
headerView.frame.size.height = 170
}
func populateMenuHeader(email: String, firstName: String, lastName: String, imageUrl: String) {
headerView?.emailLabel?.text = email
headerView?.nameLabel?.text = "\(firstName) \(lastName)"
let request = ImageRequest(
url: URL(string: imageUrl)!,
processors: [
ImageProcessor.Resize(size: CGSize(width: 70, height: 70)),
ImageProcessor.Circle()
]
)
Nuke.loadImage(with: request, into: headerView!.imageView!)
}
// Mark -- Handlers
func configureTableView() {
// Create Material Header
headerView = HeaderView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 170))
//headerView?.heightAnchor.constraint(equalToConstant: 170).isActive = true
headerView?.translatesAutoresizingMaskIntoConstraints = false
tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.tableHeaderView = headerView
tableView.sectionHeaderHeight = 170
tableView.register(MenuOptionCell.self, forCellReuseIdentifier: reuseIdentifier)
tableView.backgroundColor = .darkGray
tableView.separatorStyle = .none
tableView.rowHeight = 80
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
}
extension MenuController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! MenuOptionCell
let menuOption = MenuOption(rawValue: indexPath.row)
cell.descriptionLabel.text = menuOption?.description
cell.iconImageView.image = menuOption?.image
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = indexPath.row
print("tapped row: \(row)")
let menuOption = MenuOption(rawValue: row)
delegate?.handleMenuToggle(forMenuOption: menuOption)
}
}
class CustomView: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
if let context = UIGraphicsGetCurrentContext() {
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(1)
context.move(to: CGPoint(x: 0, y: bounds.height))
context.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
context.strokePath()
}
}
}
class HeaderView : UIView {
var imageView: UIImageView? = nil
var nameLabel: UILabel? = nil
var emailLabel: UILabel? = nil
override init(frame: CGRect) {
super.init(frame: frame)
imageView = UIImageView()
imageView?.translatesAutoresizingMaskIntoConstraints = false
nameLabel = UILabel()
nameLabel?.translatesAutoresizingMaskIntoConstraints = false
nameLabel?.font = UIFont(name: "Avenir-Light", size: 20)
nameLabel?.text = "Test name"
nameLabel?.textColor = .white
emailLabel = UILabel()
emailLabel?.translatesAutoresizingMaskIntoConstraints = false
emailLabel?.textColor = .white
emailLabel?.font = UIFont(name: "Avenir-Light", size: 15)
emailLabel?.text = "testemail#gmail.com"
self.addSubview(imageView!)
self.addSubview(nameLabel!)
self.addSubview(emailLabel!)
let lineView = CustomView(frame: CGRect(x: 0, y: frame.height - 1, width: frame.width, height: 1))
self.addSubview(lineView)
imageView?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
imageView?.topAnchor.constraint(equalTo: topAnchor, constant: 20).isActive = true
imageView?.widthAnchor.constraint(equalTo: widthAnchor, constant: 70).isActive = true
imageView?.heightAnchor.constraint(equalTo: heightAnchor, constant: 70).isActive = true
nameLabel?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
nameLabel?.topAnchor.constraint(equalTo: imageView!.bottomAnchor, constant: 10).isActive = true
emailLabel?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
emailLabel?.topAnchor.constraint(equalTo: nameLabel!.bottomAnchor, constant: 5).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The problem appears to be due to the fact that you are not properly setting the height of the header view. The documentation for tableHeaderView states:
When assigning a view to this property, set the height of that view to a nonzero value. The table view respects only the height of your view's frame rectangle; it adjusts the width of your header view automatically to match the table view's width.
Update your header view code:
Change:
headerView = HeaderView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 170))
//headerView?.heightAnchor.constraint(equalToConstant: 170).isActive = true
headerView?.translatesAutoresizingMaskIntoConstraints = false
...
tableView.sectionHeaderHeight = 170
to just:
headerView = HeaderView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 170))
That's it. No need to mess with constraints. No need to set the unrelated section height. Just give the header view's frame the desired height.
enter image description hereThere is a stretch effect.But the additional header is duplicated , only without data.Can anyone correct my code that I wrote extra or incorrectly called the header?.I have custom Header xib. link tutorial https://mobikul.com/table-view-stretch-header-parallax-animation-in-swift/
My code:
var headerView: CustomSoccerHeaderView? = {
return Bundle.main.loadNibNamed("CustomSoccerHeaderView", owner: nil, options: nil)!.first as! CustomSoccerHeaderView
}()
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem()
backButton.title = ""
self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton
// self.navigationItem.rightBarButtonItem = barButton
// self.navigationItem.rightBarButtonItem?.tintColor = UIColor.universalColorYellow
let yourBackImage = UIImage(named: "backItem")
self.navigationController?.navigationBar.backIndicatorImage = yourBackImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
self.navigationController?.navigationItem.leftItemsSupplementBackButton = true
self.navigationController?.navigationBar.tintColor = UIColor.universalColorYellow
//title = detailSoccer.detailTitleS
let nib: UINib = UINib(nibName: "CustomSoccerHeaderView", bundle: nil)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "CustomSoccerHeaderView")
tableView.tableFooterView = UIView(frame: .zero)
tableView.estimatedRowHeight = 280
headerView?.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 350)
tableView.tableHeaderView = headerView
tableView.tableHeaderView = nil
tableView.contentInset = UIEdgeInsets(top: 350, left: 0, bottom: 0, right: 0)
tableView.addSubview(headerView!)
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName:"FootTableViewCell",bundle:nil), forCellReuseIdentifier: "cellSoc")
self.view.addSubview(tableView)
tableView.rowHeight = UITableView.automaticDimension
tableView.reloadData()
loadMatchSoccer()
tableView.rowHeight = UITableView.automaticDimension
}
That's going to stretch the effect:
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
self.tableView.reloadData()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if headerView != nil {
let yPos: CGFloat = -scrollView.contentOffset.y
if yPos > 0 {
var imgRect: CGRect? = headerView?.frame
imgRect?.origin.y = scrollView.contentOffset.y
imgRect?.size.height = 350 + yPos - 350
headerView?.frame = imgRect!
self.tableView.sectionHeaderHeight = (imgRect?.size.height)!
}
}
}
This is the code for the header section of my file xib class.I think I make a mistake in inheriting the header or loading addsubview.Maybe someone more experienced can tweak my code.I would appreciate that.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomSoccerHeaderView") as! CustomSoccerHeaderView
header.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 350)
header.clickBravisso = detailSoccer
header.nameHeader.text = detailSoccer.matchS
header.countBravo.text = ""
// header.bravoBtn.addTarget(CustomSoccerHeaderView(), action: #selector(CustomSoccerHeaderView.likeBtn(_:)), for: UIControlEvents.touchUpInside)
detailSoccer.imagePrS.getDataInBackground { (data, error) in
header.imageHeader.image = error == nil ? UIImage(data: data!) : nil
}
return header
}
I have a collectionView with only 1 section in my app which download data from API. I have a pagination and I am trying to add a loading footer in my collectionView. The header appears normally. I have in this footer cell an UIActivityIndicatorView and a UILabel. When the first at a limit is triggered, the 2 elements are present in the cell but when the second limit is triggered, the UIActivityIndicatorView is absent.
Have you an idea for that.
The code of the cell (BaseCell is just a class to avoid tapping the init and required init each time):
class loadingCell: BaseCell {
override func setupViews() {
super.setupViews()
backgroundColor = .yellow
let activity = UIActivityIndicatorView()
activity.backgroundColor = .red
activity.startAnimating()
activity.frame = CGRect(x: 10, y: 20, width: 20, height: 20)
let label = UILabel()
label.text = "hello"
label.frame = CGRect(x: 100, y: 20, width: 40, height: 20)
label.backgroundColor = .green
addSubview(activity)
addSubview(label)
}
}
The collection delegate methods :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: SCREENW, height: 50)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionElementKindSectionFooter {
let loadingFooterView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionFooter, withReuseIdentifier: loadingCell, for: indexPath)
return loadingFooterView
}
return UICollectionReusableView()
}
The cell is well registered.
What happens when first limit is triggered:
And with the second:
Based on #MaksymMusiienko comment, I have tried this which do the job to reset the animation of the spinner.
class loadingCell: BaseCell {
let activitySpinner: UIActivityIndicatorView = {
let spinner = UIActivityIndicatorView()
spinner.backgroundColor = .red
spinner.startAnimating()
spinner.frame = CGRect(x: 10, y: 20, width: 20, height: 20)
return spinner
}()
override func setupViews() {
super.setupViews()
backgroundColor = .yellow
let label = UILabel()
label.text = "hello"
label.frame = CGRect(x: 100, y: 20, width: 40, height: 20)
label.backgroundColor = .green
addSubview(activitySpinner)
addSubview(label)
}
override func prepareForReuse() {
super.prepareForReuse()
activitySpinner.startAnimating()
}
}
Although there a few answers already on this topic. None of them cover Swift 3 and they are from a long time ago. What is currently the best way to change the separator height in a UITableView in Swift 3?
Updated for Swift 3:
If you want to change the height of the UITableView separator, use the code below.
You should add it to the UITableViewCell method awakeFromNib() to avoid re-creation.
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
let mScreenSize = UIScreen.main.bounds
let mSeparatorHeight = CGFloat(3.0) // Change height of speatator as you want
let mAddSeparator = UIView.init(frame: CGRect(x: 0, y: self.frame.size.height - mSeparatorHeight, width: mScreenSize.width, height: mSeparatorHeight))
mAddSeparator.backgroundColor = UIColor.brown // Change backgroundColor of separator
self.addSubview(mAddSeparator)
}
This is a correct way to do this.
First, in your ViewController you should set (tableView.separatorStyle = .none)
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
}
}
Second, in your TableViewCell class your should create a separatorView.
And don't forget to inherit TableViewCell class for your cell.
class TableViewCell: UITableViewCell {
let separator = UIView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
separator.backgroundColor = .black
contentView.addSubview(separator)
}
override func layoutSubviews() {
super.layoutSubviews()
//Your separatorLineHeight with scalefactor
let separatorLineHeight: CGFloat = 1/UIScreen.main.scale
separator.frame = CGRect(x: self.contentView.frame.origin.x,
y: self.contentView.frame.size.height - separatorLineHeight,
width: self.contentView.frame.size.width,
height: separatorLineHeight)
}
}
Finally, you've got a thin separator line and, of course, you can increase this value what do you like.
For Those who want to do it using autolayout here is the code
var additionalSeparator:UIView = UIView()
override func awakeFromNib() {
super.awakeFromNib()
self.createSeparator()
}
func createSeparator() {
self.additionalSeparator.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(self.additionalSeparator)
}
func setConstraintForSeparator() {
self.additionalSeparator.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: self.separatorInset.left).isActive = true
self.additionalSeparator.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -self.separatorInset.right).isActive = true
self.additionalSeparator.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: 0).isActive = true
self.additionalSeparator.heightAnchor.constraint(equalToConstant: 1).isActive = true
self.additionalSeparator.backgroundColor = UIColor.greyishBrown
}
Try this Swift 3:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: YOUR_CELL_IDENTIFIER, for: indexPath) as! yourTableViewCell
let viewSeparatorLine = UIView(frame:CGRect(x: 0, y: cell.contentView.frame.size.height - 5.0, width: cell.contentView.frame.size.width, height: 5))
viewSeparatorLine.backgroundColor = .red
cell.contentView.addSubview(viewSeparatorLine)
return cell
}
I want to create a custom UITableViewCell programmatically and cells have different height. But the height is fixed.
SingeTableViewCell.swift
class SingleTableViewCell: UITableViewCell {
var title = UILabel()
var content = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
self.setSeparator()
self.setTitle()
self.setContent()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setSeparator() {
let separator = UIView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, 10))
separator.backgroundColor = UIColor(hex: "#E7EBF2")
contentView.addSubview(separator)
}
func setTitle() {
let viewTitle = UIView(frame: CGRectMake(0, 10, UIScreen.mainScreen().bounds.width, 40))
viewTitle.backgroundColor = UIColor(hex: "#EEE")
viewTitle.layer.borderColor = UIColor.lightGrayColor().CGColor
viewTitle.layer.borderWidth = 1.0
self.title = UILabel(frame: CGRectMake(5, 0, UIScreen.mainScreen().bounds.width-5, 40))
self.title.textAlignment = NSTextAlignment.Center
self.title.font = self.title.font.fontWithSize(13)
viewTitle.addSubview(self.title)
contentView.addSubview(viewTitle)
}
func setContent() {
self.content = UILabel(frame: CGRectMake(0, 50, UIScreen.mainScreen().bounds.width, 400))
self.backgroundColor = UIColor.whiteColor()
contentView.addSubview(self.content)
}
}
I want cell's height is equals to frame's height.
Thank you and sorry for my bad english.
You can use Auto Layout to set self-sizing cell. Working with Self-Sizing Table View Cells
Alternatively, you can set the height with - tableView:heightForRowAtIndexPath: in UITableViewDelegate
class ViewController: UIViewController, UITableViewDelegate{
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// assume that you want to set height equal to 300 in 1st secion, and 200 in 2nd section.
let heights: [CGFloat] = [ 300,200 ]
return heights[indexPath.section]
}
}