I have been trying to develop a view controller with multiple charts(bar chart, line chart, pie chart). I created a table view and custom table view cell. There is a UIView inside custom table view cell. However, when I am trying to cast that UIView to BarchartView it gives me an error
Could not cast value of type 'UIView' (0x10a8e7f40) to 'Charts.LineChartView' (0x1074f63a0).
How can I achieve that multiple charts in same table view? Thanks in advance.
cellForRowAt indexPath:
let cell = myTableView.dequeueReusableCell(withIdentifier: "chart") as? GraphicCell
var lineChart:LineChartView = cell?.chart as! LineChartView
lineChart.noDataText = "A"
return cell!
the view outlet that I have created in GraphicCell is UIView type
The charts which are shown depends on the user choice. User can select one bar chart and two line chart or only two line chart without bar chart. I do not understand completely how to achieve this. I have added my project to GitHub project link
You need to create prototype cells for each of the types of charts, which you want to use in your TableView. In each prototype cell you need to put UIView and then change the class of UIView to LineChartView, BarChartView, etc.
Also you need to define your own class for each prototype cell, e.g:
class MyLineChartCell: UITableViewCell {
#IBOutlet weak var lineChartView: LineChartView!
func configure (/* ... */) {
lineChartView.noDataText = "You have no data"
// ...
}
}
.
Use this classes for you prototype cells:
Then in func func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell you could choose which prototype will be used at the moment.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyLineChartCellIdentifier") as! MyLineChartCell
cell.configure(/* Data, Colors, etc. */)
}
if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyBarChartCellIdentifier") as! MyBarChartCell
cell.configure(/* Data, Colors, etc. */)
}
//... some other types of cells
return cell
}
The chartView canĀ“t be of type UIView, it has to have the correct type when you declare it. You can have 3 different views inside the tableView cell, like this:
#IBOutlet weak var barChart: BarChartView!
#IBOutlet weak var pieChart: PieChartView!
#IBOutlet weak var lineChart: LineChartView!
and then use the one you need, depending on which graph type you want. If you are using Storyboard, you also need to choose class for each view as BarChartView, LineChartView or PieChartView in the Storyboard.
Related
Im Developing an App and I have some few problems with my tableviewcontroller.First of all, left to the picture there is free space.How can I get the Image bigger ? or how can I get this so that there is no free space. The image is a cell.imageview and its getting his image data from core data.
And the next problem is. I have some problems with the rows, you can see this on the picture.How can I fix this ?
It seems that you use the default UITableViewCell class , but you need to create a subclass of it and make every content custom
regrading label trimming you need to set .numberOfLines property of the UILabel item to 0 so it can wrap , a review for constraints may be like this
ImageView : width , height (static/propertional) , top , leading
label : leading to imageView , top - trailing and bottom to cell
This Using Auto Layout in UITableView for dynamic cell layouts & variable row heights may help you in creating the dynamic table cell
First of all, it would be much more constructive for you to implement a subclass of UITableViewCell and specify any cell-related logic in there. An intuitive approach - based on the contents of your table view screenshot - would look something like this
class ShowTableViewCell: UITableViewCell {
// MARK: - Outlets
#IBOutlet weak var imgThumbnail: UIImageView!
#IBOutlet weak var lblTitle: UILabel!
// MARK: - Functions
func setUp(withShow show: Show) {
self.imgThumbnail.image = show.thumbnailImage
self.lblTitle.text = show.title
}
}
Note that the setUp(withShow:) function declared above is using a user-defined object of type Show
class Show {
// MARK: - Properties
var title: String?
var thumbnailImage: UIImage?
// MARK: - Initializers
public init(withTitle title: String, andImage image: UIImage) {
self.title = title
self.thumbnailImage = image
}
}
In my opinion, the latter approach conforms much better with Object Oriented Programming (OOP) standards. In case you need to add/remove any property to your Show object, it will be so much easier and cleaner to reflect those changes within your cells. All you would have to do is add/remove the respective #IBOutlet objects from your cell and configure them just as demonstrated in the setUp(withShow:) class function.
Moving on, you implement the following view controller that conforms to the UITableViewDataSource protocol, and populate your table accordingly
class ViewController: UIViewController {
// MARK: - Properties
var shows = [
Show(withTitle: "UFC 127: Penn vs. Fitch", andImage: RESPECTIVE_IMAGE),
Show(withTitle: "Strangers in Good Company", andImage: RESPECTIVE_IMAGE),
Show(withTitle: "Candles on Bay Street", andImage: RESPECTIVE_IMAGE),
Show(withTitle: "Flight Angels", andImage: RESPECTIVE_IMAGE)
]
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
}
}
// MARK: - UITableViewDataSource Extension
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "showCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ShowTableViewCell
cell.setUp(withShow: shows[indexPath.row])
return cell
}
}
Note that you when declaring the shows array, you should replace the RESPECTIVE_IMAGE placeholder I have added with whatever image you want. You should also populate it with whatever static elements you want - or more dynamically - query them from your database.
Additionally, using auto-layout, you can manually determine the size of your image and, using the Number of Lines label attribute, you can prevent your labels from trimming.
Here is my implementation of tableView(_:cellForRowAt:):
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.section
let weekDay = WeekDays.day(at: index)
if self.availability.numberOfTimeslots(for: weekDay) == 0 {
let cell = NotSelectedCell(style: .default, reuseIdentifier: nil)
return cell
}
return UITableViewCell()
}
Here is my code for my custom table view cell:
class NotSelectedCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.backgroundColor = .red
self.textLabel?.numberOfLines = 0
self.textLabel?.textAlignment = .center;
self.textLabel?.text = "Not Available"
}
}
I've also tried initializing custom cell cell = NotSelectedCell() the result is the same. The content isn't shown. dataSource or viewDelegate aren't the problem as I'm working with UITableViewController.
Here's an image
The problem is awakeFromNIB "prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file." But you're instantiating this programmatically, so that method isn't called. You could theoretically move the code to init(style:reuseIdentifier:), make sure to call super in your implementation, and do any additional customization after that point.
But, you generally wouldn't programmatically instantiate cells when using static cells. (It's the point of static cells, that IB takes care of everything for you.) You generally don't implement UITableViewDataSource at all when using static cells.
I would advise using dynamic table and have two cell prototypes, one with reuse identifier of "NotAvailable" and one with "Available" (or whatever identifiers you want). Then programmatically instantiate the cell with the appropriate identifier. (By the way, this also has the virtue that your cell with "NotAvailable" can be designed entirely in IB, and no code is needed, for that cell at least.) This way, the storyboard takes care of instantiating the appropriate cell.
So, here I have two cell prototypes in my dynamic table, one for "not available" and one for "available":
Then the code would look at the model to figure out which to instantiate:
// for the complicated cell where I want to show details of some window of availability, add IBOutlets for that cell's labels
class AvailableCell: UITableViewCell {
#IBOutlet weak var startLabel: UILabel!
#IBOutlet weak var stopLabel: UILabel!
#IBOutlet weak var doctorLabel: UILabel!
}
// some super simple model to represent some window of availability with a particular doctor in that office
struct Availability {
let start: String
let stop: String
let doctor: String
}
class ViewController: UITableViewController {
let days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
let available = ...
override func numberOfSections(in tableView: UITableView) -> Int {
return days.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return available[days[section]]?.count ?? 1
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return days[section]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// see if there are any available windows for the given day, if not, return "not available" cell
guard let availabilities = available[days[indexPath.section]] else {
return tableView.dequeueReusableCell(withIdentifier: "NotAvailable", for: indexPath)
}
// otherwise, proceed with the more complicated "Available" cell where I have to populate various labels and the like
let cell = tableView.dequeueReusableCell(withIdentifier: "Available", for: indexPath) as! AvailableCell
let availability = availabilities[indexPath.row]
cell.startLabel.text = availability.start
cell.stopLabel.text = availability.stop
cell.doctorLabel.text = availability.doctor
return cell
}
}
And that would yield:
Now, clearly, I just whipped up a super primitive model, and didn't do any UI design in the "available" cell prototype other than inserting three labels. But it illustrates the idea: If your dynamic table has multiple unique cell designs, just implement cell prototypes for each with unique identifiers and instantiate the appropriate one. And this way, you enjoy full cell reuse, minimize how much visual design you have to do programmatically, etc.
You are not supposed to use the cellForRow:atIndexPath method when using static cells. The cells are static, so the loading flow is different. What i'd suggest is to connect the cells individually from the interface builder to your view controller.
STILL, if you want to do it this way you have to get your cells by calling "super" since that's the class who is actually generating your static cells.
UITableView with static cells without cellForRowAtIndexPath. How to set clear background?
EDIT:
I just noticed that this is wrong:
if self.availability.numberOfTimeslots(for: weekDay) == 0 {
let cell = NotSelectedCell(style: .default, reuseIdentifier: nil)
return cell
}
You have to use the "dequeueReusable" method or something. Then again, these are STATIC Cells, so you should just be linking the cells directly from the interface builder.
I have a custom cell class called CurrentFilesCell with the setting code below
class CurrentFileCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var statusImage: UIImageView!
var currentContent: AircraftContent! {
didSet{
setStyles(Constants.appStyleSetting)
nameLabel.text = currentContent.contentName
dateLabel.text = currentContent.contentStatus
}
}
Within my CurrentFilesViewController I simply set it within cellForRowAtIndexPath
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CurrentFileCell", forIndexPath: indexPath) as? CurrentFileCell
cell?.currentContent = content
return cell!
}
I believe I also have everything linked correctly, as I have done something similar to this in other classes, both with cells and vc's.
My problem is that It does not load anything when run, there is no default text and no updated text after it should have been set. Here is an image showing the linkage
http://imgur.com/qlK4d5O
I'm really not sure what is going on and why this isn't working. I have tried deleting it and recreating but I must be missing something.
EDIT
Here is a picture of the debugger showing that the cell's currentContent is not empty. This is taken right before the return cell! is executed.
http://imgur.com/O250qXq
Did you register this cell in table view? If not than dqueRqusableCellWithIdentifier will return nil value...
You can register it using UITableView function "registerNib: forCellReuseIdentifier:"
In the storyboard, you must define subclass of the prototype table cell.
And then, you must define identifier of the prototype table cell as "CurrentFileCell".
Then you will show the content of the table when the app will be run.
I have an array of UIImageViews that I want to display to the screen. However, I can't seem to be able to do so. I declared my array of UIImageViews like so:
class EventCell: UITableViewCell, CellDelegate{
#IBOutlet var eventName: UILabel!
#IBOutlet var eventLocation: UILabel!
#IBOutlet var attendeesImages: [UIImageView]!
}
And I have a function that displays the EventCell contents like so:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Dequeue a "reusable" cell
let cell = tableView.dequeueReusableCellWithIdentifier(eventCellIdentifier) as! EventCell
setCellContents(cell, indexPath: indexPath)
return cell
}
//Set contents of Event Cell.. self.events is a global array
//which have information that EventCell objects need to display
func setCellContents(cell:EventCell, indexPath: NSIndexPath!){
let item = self.events[indexPath.section]
var count = 0
cell.eventName.text = item.eventName()
cell.eventLocation.text = item.eventLocation()
cell.attendeesImage.removeAll(keepCapacity: false)//Remove old images before adding new
for value in item.attendeesImage {
let newImageView = UIImageView(image : value)
newImageView.clipsToBounds = true
newImageView.layer.cornerRadius = newImageView.frame.size.width / 2
cell.attendeesImage.append(newImageView)
println("Count inside: \(cell.attendeesImage.count)")
}
}
I printed out the count of the cell.attendeesImage to ensure that no extraneous images were being added and it seemed to prove correct. However, I have no idea why my images are not displaying. I made sure to hook the cell.attendeesImage as an IBCollection in my Storyboard (since it is an Array) in my EventCell. Any ideas for why this isn't showing up? Thanks!
You are using wrong the collection outlet "attendeesImages".
When the view is loaded, the array attendeesImages are created and initialized with the image views you have created in interface builder. But then, if you remove all that references, you have lost the link between the outlets and the real views. The new image views you are creating are not the ones in the cell. They are still subviews of the cell, but now you have not a iboutlet references to them.
The solution: don't remove the values from the array. Just modify the image in each image view.
I have a basic method for populating a tableView with a custom cell in Swift
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let object = array[UInt(indexPath!.row)] as Word
var cell:DictionaryTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("dictionaryCell") as DictionaryTableViewCell
cell.originalText.text = object.original
return cell
}
Works fine. Now, I got a custom cell DictionaryTableViewCell with custom properties and some IBActions (for different buttons inside that cell).
My question is: How do I expose data that was passed to the cell from tableView and use it from inside my custom cell controller? For instance I would like to be able to manipulate it for each cell using IBActions.
For now my custom cell controller is nothing fancy
class DictionaryTableViewCell: UITableViewCell {
#IBOutlet weak var originalText: UILabel!
#IBAction func peak(sender: AnyObject) {
}
}
Is there a custom property I can use to recover data passed onto it? Or do I have to implement some sort of a protocol? If so - how?