I have a UITableView under a UIViewController with a custom cell. The text for the UILabel in each cell is held in an array of strings. I’m coding in Swift Playgrounds, and when I run the Playground with an empty array it works fine (there aren’t an cells, of course, but the playground does run). When I populate the array, I get the error “there as a problem running this page... check your code...”. When I step through the code, it gets stuck at the line:
view.addSubview(gradientView)
What am I doing wrong?
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
// Array that holds menu items
var menuItems = ["option 1","option 2","option 3"]
override func viewDidLoad() {
super.viewDidLoad()
// Add a gradient background
// Set height, width to view height, width
var gradientView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height))
let gradientLayer:CAGradientLayer = CAGradientLayer()
gradientLayer.frame.size = gradientView.frame.size
// Set colors
gradientLayer.colors = [UIColor(red: 253/255, green: 94/255, blue: 172/255, alpha: 1).cgColor, UIColor(red: 121/255, green: 73/255, blue: 242/255, alpha: 1).cgColor]
// Skew gradient (diagonally)
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
// Rasterize to improve performance
gradientLayer.shouldRasterize = true
//Add gradient
gradientView.layer.addSublayer(gradientLayer)
view.addSubview(gradientView)
// Reguster custom cell
tableView.register(MenuItemCell.self, forCellReuseIdentifier: "cell_1")
// Turn off seperators
tableView.separatorStyle = .none
// Set header height
tableView.sectionHeaderHeight = 75
}
// Custom header
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?{
let customView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 75))
customView.backgroundColor = .clear //UIColor(white: 0.9, alpha: 1)
let button = UIButton(type: .custom)
button.setTitle("😁", for: .normal)
button.frame = CGRect(x: 20, y: 20, width: 50, height: 50)
button.layer.cornerRadius = 25
button.layer.shadowRadius = 8.0
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOffset = CGSize(width: 0, height: 0)
button.layer.shadowOpacity = 0.5
let blur = UIVisualEffectView(effect: UIBlurEffect(style:
UIBlurEffect.Style.light))
blur.frame = button.bounds
blur.isUserInteractionEnabled = false //This allows touches to forward to the button.
button.insertSubview(blur, at: 0)
customView.addSubview(button)
return customView
}
#objc func updateView(){
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_1", for: indexPath) as! MenuItemCell
cell.selectionStyle = .none
//cell.messageLabel.text = textMessages[indexPath.row]
cell.bubbleBackgroundView.backgroundColor = UIColor(white: 0.9, alpha: 1)
return cell
}
}
class MenuItemCell: UITableViewCell {
let optionLabel = UILabel()
let bubbleBackgroundView = UIView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
bubbleBackgroundView.layer.shadowOpacity = 0.35
bubbleBackgroundView.layer.shadowRadius = 6
bubbleBackgroundView.layer.shadowOffset = CGSize(width: 0, height: 0)
bubbleBackgroundView.layer.shadowColor = UIColor.black.cgColor
bubbleBackgroundView.layer.cornerRadius = 25
bubbleBackgroundView.translatesAutoresizingMaskIntoConstraints = false
addSubview(bubbleBackgroundView)
addSubview(optionLabel)
optionLabel.numberOfLines = 0
optionLabel.translatesAutoresizingMaskIntoConstraints = false
// lets set up some constraints for our label
let constraints = [optionLabel.topAnchor.constraint(equalTo: topAnchor, constant: 32),
optionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32),
optionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -32),
optionLabel.widthAnchor.constraint(equalToConstant: 250),
bubbleBackgroundView.topAnchor.constraint(equalTo: optionLabel.topAnchor, constant: -16),
bubbleBackgroundView.leadingAnchor.constraint(equalTo: optionLabel.leadingAnchor, constant: -16),
bubbleBackgroundView.bottomAnchor.constraint(equalTo: optionLabel.bottomAnchor, constant: 16),
bubbleBackgroundView.trailingAnchor.constraint(equalTo: optionLabel.trailingAnchor, constant: 16),
]
NSLayoutConstraint.activate(constraints)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
PlaygroundPage.current.liveView = ViewController()
Related
I have a tableView that displays hidden cells when the user scrolls. Not sure why this behavior is happening.
In viewDidLoad()
watchListTable = UITableView(frame: CGRect(x: self.view.frame.width * 0.25, y: 0, width: self.view.frame.width * 0.75, height: 300)) //height = 200
watchListTable.isHidden = true
watchListTableFrame = CGRect(x: self.view.frame.width * 0.25, y: 0, width: self.view.frame.width * 0.75, height: 300)
watchListTableFrameHide = CGRect(x: self.view.frame.width * 0.25, y: 0, width: self.view.frame.width * 0.75, height: 0)
watchListTable.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
watchListTable.register(UITableViewCell.self, forCellReuseIdentifier: "closeCell")
watchListTable.dataSource = self
watchListTable.delegate = self
watchListTable.CheckInterfaceStyle()
watchListTable.roundCorners(corners: .allCorners, radius: 8)
watchListTable.backgroundColor = .systemGray6
//remove the bottom line if there is only one option
watchListTable.tableFooterView = UIView()
view.addSubview(watchListTable)
Once the user taps on a button, the table expands in an animatable fashion.
//watchlist won't animate properly on the initial setup. So we set it to be
hidden, then change the frame to be 0, unhide it, and then animate it. Only will
be hidden on the initial setup.
if(watchListTable.isHidden == true)
{
watchListTable.isHidden = false
watchListTable.frame = watchListTableFrameHide
}
UIView().animateDropDown(dropDown: watchListTable, frames:
self.watchListTableFrame)
watchListTable.reloadData()
In func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
if(indexPath.row >= watchListStocks.count)
{
let cell = tableView.dequeueReusableCell(withIdentifier: "closeCell",
for: indexPath as IndexPath)
cell.selectionStyle = .none
cell.textLabel?.text = indexPath.row == watchListStocks.count + 1 ?
"Close List" : "Create New Watchlist"
cell.textLabel?.textColor = .stockOrbitTeal
cell.textLabel?.textAlignment = .center
cell.backgroundColor = .systemGray6
cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right:
.greatestFiniteMagnitude)
return cell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for:
indexPath as IndexPath)
cell.selectionStyle = .none
if(indexPath.row == 0)
{
cell.layer.cornerRadius = 8
cell.layer.maskedCorners = [.layerMinXMinYCorner,
.layerMaxXMinYCorner]
}
else
{
cell.layer.cornerRadius = 8
cell.layer.maskedCorners = [.layerMinXMaxYCorner,
.layerMaxXMaxYCorner]
cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right:
.greatestFiniteMagnitude)
cell.directionalLayoutMargins = .zero
}
let label = UITextView()
label.frame = CGRect(x: 0, y: 0, width: cell.frame.width * 0.45, height:
cell.frame.height)
label.text = watchListStocks[indexPath.row].listName
label.textColor = .stockOrbitTeal
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 18, weight: UIFont.Weight.medium)
label.backgroundColor = .systemGray5
label.delegate = self
label.tag = indexPath.row
cell.addSubview(label)
cell.backgroundColor = .systemGray5
cell.layer.cornerRadius = 8
return cell
}
When I scroll, all cells are hidden. I see that they are created in cellForRowAt, however, they do not appear on my screen. Why are the cells being hidden? I have searched all over stackoverflow.
You shouldn't add subviews inside cellForRowAt. When you call dequeueReusableCell, at first it'll create new cells, but when you start scrolling it'll start returning cells that were dismissed earlier, means they already have UITextView subview, and you're adding one more on top of that.
cell returned by dequeueReusableCell doesn't have to have final size already, that's why you can't use cell.frame.width to calculate your subview size, I think that's may be the reason you can't see it.
What you need to do: create a UITableView subclass, something like this:
class MyCell: UITableViewCell {
let label = UITextView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupCell()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupCell()
}
func setupCell() {
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 18, weight: UIFont.Weight.medium)
label.backgroundColor = .systemGray5
contentView.addSubview(label)
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame = CGRect(x: 0, y: 0, width: contentView.frame.width * 0.45, height: contentView.frame.height)
}
}
Here you're adding a subview during initialisation only once and update label frame each time cell size gets changed. Don't forget to add this class to your cell in the storyboard and let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath as IndexPath) as! MyCell, so you can set delegate to text field, etc.
If this won't help, check out View Hierarchy to see what's actually going on there
So after many hours, I figured it out...
I had called this function in viewDidLoad()
watchListTable.roundCorners(corners: .allCorners, radius: 8)
Which made my table hidden after I scrolled. I removed this line of code, and the table is now completely visible when scrolling.
I have a uitableview
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let width = tableView.frame.width - CGFloat((start*2))
let headerView = UIView()
headerView.frame.origin.x = 0
headerView.frame.origin.y = -45
headerView.frame.size.width = tableView.frame.width
let desc = UILabel()
desc.frame.origin.x = label.frame.origin.x
desc.frame.origin.y = line.frame.maxY + 10
desc.frame.size.width = width
desc.numberOfLines = 0
desc.font = UIFont(name: "Lato-Regular", size: 17);
desc.textColor = UIColor(red: 0.53, green: 0.53, blue: 0.53, alpha: 1.00)
desc.text = self.data.desc
desc.backgroundColor = UIColor.clear
desc.textAlignment = .left
desc.adjustsFontForContentSizeCategory = true
if (loadMoreDesc) {
desc.sizeToFit()
} else {
desc.frame.size.height = 110
}
headerView.addSubview(desc)
let moreButtonDesc = UIButton(frame: CGRect(x: desc.frame.minX, y: desc.frame.maxY, width: 80, height: 15))
moreButtonDesc.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center
moreButtonDesc.titleLabel?.textAlignment = .left
if (loadMoreDesc) {
moreButtonDesc.setTitle("Less", for: .normal)
} else {
moreButtonDesc.setTitle("More", for: .normal)
}
moreButtonDesc.titleLabel?.font = UIFont(name: "Lato-Regular", size: 15);
moreButtonDesc.setTitleColor(UIColor(red: 0.00, green: 0.48, blue: 1.00, alpha: 1.00), for: .normal)
moreButtonDesc.addTarget(self, action:#selector(self.expandDesc(sender:)), for: .touchUpInside)
headerView.addSubview(moreButtonDesc)
When people click on "More" button the description should expand and the height of the table view header should increase to fit the description (desc). How can I accomplish this? Here's my expandDesc function
#objc func expandDesc(sender: UIButton) {
loadMoreDesc = !loadMoreDesc
tableView.reloadData()
}
Please note that the description is of dynamic height. I won’t know the height to set for the header unless I know the height of the description.
please can you try this
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if loadMoreDesc {
return 100
} else {
return 50.0
}
}
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.
When certain buttons are pressed in the app, their name, start and end time that they were pressed are displayed in a UITableView.
This worked fine when using a custom UITableViewCell but after setting up tableView.dequeueReusableCell instead, the UITableView is showing the first cell as a white empty cell when it is meant to show data. If more data is added, the first input which wasn't visible is now shown but the last input is missing/hidden.
I have found similar questions and implemented what seemed the main culprit but it didn't work for me.
timelineTableView.contentInsetAdjustmentBehavior = .never
timelineScrollViewContainer.contentInsetAdjustmentBehavior = .never
timelineTableView.contentOffset = .zero
I also tried to change the section height but to no avail either.
Worth mentioning that the data is not displaying properly in the UITableView but is still saving properly in the plist.
The UITableView is loaded during the ViewDidLoad as mentioned in other questions as it seems the issue of the error for some.
Doeanyonene have another solution? thanks for the help
cellForRowAt method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == timelineTableView {
//let cell = TimelineCell(frame: CGRect(x: 0, y: 0, width: 100, height: 40), title: "test", startTime: "test", endTime: "test", rawStart: "") // Used previously before using dequeueReusableCell
var cell: TimelineCell = tableView.dequeueReusableCell(withIdentifier: timelineCellId, for: indexPath) as! TimelineCell
if let marker = markUpPlist.arrayObjects.filter({$0.UUIDpic == endClipSelectedMarkerUUID}).first {
cell = TimelineCell(frame: CGRect(x: 0, y: 0, width: 100, height: 40), title: "test", startTime: "test", endTime: "test", rawStart: "")
cell.backgroundColor = marker.colour
cell.cellLabelTitle.text = marker.name
cell.cellUUID.text = marker.UUIDpic
if let timeline = chronData.rows.filter({$0.rowName == marker.name}).first {
if let start = timeline.clips.last?.str {
cell.cellStartTime.text = chronTimeEdited(time: Double(start))
cell.cellStartRaw.text = String(start)
}
if let end = timeline.clips.last?.end {
cell.cellEndTime.text = chronTimeEdited(time: Double(end))
}
}
}
return cell
}
TimelineCell.swift
class TimelineCell : UITableViewCell {
var cellLabelTitle: UILabel!
var cellStartTime: UILabel!
var cellEndTime: UILabel!
var cellStartRaw: UILabel!
var cellUUID: UILabel!
init(frame: CGRect, title: String , startTime: String, endTime: String, rawStart: String) {
super.init(style: UITableViewCell.CellStyle.default, reuseIdentifier: "timelineCellId")
backgroundColor = UIColor(red: 29/255.0, green: 30/255.0, blue: 33/255.0, alpha: 1.0)
cellLabelTitle = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
cellLabelTitle.translatesAutoresizingMaskIntoConstraints = false
cellLabelTitle.textColor = UIColor.black
addSubview(cellLabelTitle)
cellLabelTitle.widthAnchor.constraint(equalToConstant: 80).isActive = true
cellLabelTitle.heightAnchor.constraint(equalToConstant: 30).isActive = true
cellLabelTitle.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
cellLabelTitle.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true
cellStartTime = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
cellStartTime.translatesAutoresizingMaskIntoConstraints = false
cellStartTime.textColor = UIColor.black
addSubview(cellStartTime)
cellStartTime.widthAnchor.constraint(equalToConstant: 80).isActive = true
cellStartTime.heightAnchor.constraint(equalToConstant: 30).isActive = true
cellStartTime.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
cellStartTime.leftAnchor.constraint(equalTo: cellLabelTitle.rightAnchor, constant: 10).isActive = true
cellEndTime = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
cellEndTime.translatesAutoresizingMaskIntoConstraints = false
cellEndTime.textColor = UIColor.black
addSubview(cellEndTime)
cellEndTime.widthAnchor.constraint(equalToConstant: 80).isActive = true
cellEndTime.heightAnchor.constraint(equalToConstant: 30).isActive = true
cellEndTime.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0).isActive = true
cellEndTime.leftAnchor.constraint(equalTo: cellStartTime.rightAnchor, constant: 10).isActive = true
cellStartRaw = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
cellUUID = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
addSubview(cellStartRaw)
addSubview(cellUUID)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
Create tableview
timelineTableView.frame = CGRect(x: 0, y: 0, width: sideView.frame.width, height: sideView.frame.size.height)
timelineTableView.delegate = self
timelineTableView.dataSource = self
timelineTableView.register(TimelineCell.self, forCellReuseIdentifier: timelineCellId)
timelineTableView.translatesAutoresizingMaskIntoConstraints = false
timelineTableView.separatorStyle = .none
timelineTableView.backgroundColor = Style.BackgroundColor
timelineTableView.contentInsetAdjustmentBehavior = .never
timelineScrollViewContainer.contentInsetAdjustmentBehavior = .never
timelineScrollViewContainer.addSubview(timelineTableView)
timelineTableView.contentOffset = .zero
So recapitulating, using the line below shows the data properly but the cells aren't reused properly.
let cell = TimelineCell(frame: CGRect(x: 0, y: 0, width: 100, height: 40), title: "test", startTime: "test", endTime: "test", rawStart: "")
Using the code below show a blank cell first and data not displayed properly but the cells are reused properly.
var cell: TimelineCell = tableView.dequeueReusableCell(withIdentifier: timelineCellId, for: indexPath) as! TimelineCell
Change cellForRowAt to look like this. I'm guessing as to how your chronData structure relates to the source table, so it's mostly using your original logic.
You need to blank out fields that should be empty, as otherwise they will retain state as you scroll.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: TimelineCell = tableView.dequeueReusableCell(withIdentifier: timelineCellId, for: indexPath) as! TimelineCell
let marker = markUpPListFiltered
cell.backgroundColor = marker.colour
cell.cellLabelTitle.text = marker.name
cell.cellUUID.text = marker.UUIDpic
if let timeline = chronData.rows.filter({$0.rowName == marker.name}).first {
if let start = timeline.clips.last?.str {
cell.cellStartTime.text = chronTimeEdited(time: Double(start))
cell.cellStartRaw.text = String(start)
}
else
{
cell.cellStartTime.text = ""
cell.cellStartRaw.text = ""
}
if let end = timeline.clips.last?.end {
cell.cellEndTime.text = chronTimeEdited(time: Double(end))
}
else
{
cell.cellEndTime.text = ""
}
}
return cell
}
It assumes there is an array for your filtered sorted data called markupPListFiltered. Prepare this in viewDidLoad or somewhere else. You haven't shown the other datasource methods so I'll assume you can change these as needed, e.g.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return markupPListFiltered.count
}
TimelineCell needs the c'tor to have the data removed. You should consider using a storyboard to design your cells and link up the widgets with the outlets (see any of the hundreds of tutorials on table views, I'd recommend Ray Wenderlich as a good starter source).
I'm designing my UI programmatically, I'm creating some buttons and then showing in UICollectionView. after showing in UICollectionView when I checked, each button is pointing to tag 0.
Here's my complete code code.
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
var buttonsArray = [UIButton]()
let buttonsName = ["Start Trade", "Wallet","Profile", "My Portfolio", "Dashboard","My Transactions","My Blotter" ,"My Reports", "Forum"]
let buttonImages = ["1","2","3", "4", "5", "6", "7","8", "9"]
var collectionview: UICollectionView!
var cellId = "Cell"
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<9 {
let btn = UIButton()
btn.tag = i
btn.frame = CGRect(x: btn.frame.width/2, y:btn.frame.width/2, width: 106, height: 97)
btn.backgroundColor = UIColor(red: 0.15, green: 0.22, blue: 0.68, alpha: 0.86)
btn.layer.cornerRadius = 5
btn.titleLabel?.font = UIFont.setFont(of: 12)
btn.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
btn.setTitleColor(UIColor(red: 0.82, green: 0.56, blue: 0.23, alpha: 1), for: .normal)
btn.setTitle(buttonsName[i], for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
buttonsArray.append(btn)
}
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 106, height: 97)
collectionview = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionview.dataSource = self
collectionview.delegate = self
collectionview.register(MainDashBoardCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionview.showsVerticalScrollIndicator = false
collectionview.backgroundColor = view.backgroundColor
self.view.addSubview(collectionview)
}
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
sender.animateButton(sender: sender)
print(sender.tag)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return buttonsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MainDashBoardCollectionViewCell
cell.images.image = buttonImages[indexPath.item]
cell.button = buttonsArray[indexPath.item]
cell.Label0.text = buttonsName[indexPath.item]
return cell
}
Here's My CollectionViewCell class
import UIKit
class MainDashBoardCollectionViewCell: UICollectionViewCell {
var button :UIButton = {
let btn = UIButton()
btn.frame = CGRect(x: btn.frame.width/2, y:btn.frame.width/2, width: 106, height: 97)
btn.backgroundColor = UIColor(red: 0.15, green: 0.22, blue: 0.68, alpha: 0.86)
btn.layer.cornerRadius = 5
btn.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
btn.titleLabel?.font = UIFont.setFont(of: 12)
btn.setTitleColor(UIColor(red: 0.82, green: 0.56, blue: 0.23, alpha: 1), for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
var images: UIImageView = {
let imgV = UIImageView()
imgV .translatesAutoresizingMaskIntoConstraints = false
return imgV
}()
let Label0: UILabel = {
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 60, height: 15)
label.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
label.text = ""
label.font = UIFont.setFont(of: 12)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(images)
addSubview(button)
addSubview(Label0)
shared()
}
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
print("clicked")
print("you clicked on button \(sender.tag)")
}
func shared() {
self.contentView.addSubview(button)
self.contentView.addSubview(images)
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
button.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
button.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
])
images.widthAnchor.constraint(equalToConstant: 26).isActive = true
images.heightAnchor.constraint(equalToConstant: 28).isActive = true
images.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
images.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: -10).isActive = true
Label0.centerXAnchor.constraint(equalTo: images.centerXAnchor).isActive = true
Label0.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: 20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
//super.init(aDecoder)
fatalError("init(coder:) has not been implemented")
}
}
is anything wrong with my code? Please help me to fix it.
Thanks in advance.
By default every UI component have tag 0, so you have to change the tag of every button.
Try this in cellForItem
cell.button.tag = indexPath.row