Swift index 0 beyond bounds for empty array in tableview - ios

I'm trying to populate tableview with parse with 2 labels connected to the the main tv controller with PFTableViewCell
when I add the (numberOfSectionsInTableView + numberOfRowsInSection ) the app crash
but when I deleted it it works but it show nothing.
This the table view cell
class courseCell: PFTableViewCell {
#IBOutlet var name: UILabel!
#IBOutlet var location: UILabel!
}
This the table view controller
class courseTVC: PFQueryTableViewController {
override init!(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder:NSCoder)
{
super.init(coder:aDecoder)
self.parseClassName = "courses"
self.textKey = "Location"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
override func queryForTable() -> PFQuery! {
var query = PFQuery(className: "courses")
query.orderByDescending("Location")
return query
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!, object: PFObject!) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell" , forIndexPath : indexPath) as? courseCell
if cell == nil
{
cell = courseCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell!.name.text = object["Price"] as! String!
cell!.location.text = object["Location"] as! String!
return cell!
}
I don't know how to fix this issue

Looking at the documentation for PFQueryTableViewController, https://www.parse.com/docs/ios/api/Classes/PFQueryTableViewController.html it looks like you shouldn't be overriding those two methods.
– tableView:cellForRowAtIndexPath:object: should be called already for all the rows in your table based on your objects. You shouldn't override numberOfSectionsInTableView and numberOfRowsInSection because the Parse controller handles all of that for you. You are overriding these methods and hardcoding a number which causes Parse to try to fetch and object for a row that is out of bounds (it doesn't exist). It looks like there are actually no objects in your datasource.

Related

Filtering query for Realm

I have a function which prints all the objects in my realm table to a table view. I would like to be able to filter these objects by their "muscle" property.
Here's my DB helper functions:
func getMusclesCount()-> Int {
let storedExercise = realm.objects(StoredExercise.self)
return storedExercise.count
}
//MARK:- getAllMuscelsNames
func getAllMusclesNames()-> [String] {
var musclesName = [String]()
let storedExercise = realm.objects(StoredExercise.self)
for exercise in storedExercise {
print("Muscle = \(exercise.muscle)")
musclesName.append(exercise.name)
}
return musclesName
}
Here's my Table View Controller class :
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DBHelper.shared.getAllMusclesNames().count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
let muscle = DBHelper.shared.getAllMusclesNames()[indexPath.row]
cell.textLabel?.text = muscle
return cell
}
I've tried adding .Filter to 'let storedExercise' but I'm not sure how to set it up correctly. Any assitance would be greatly appreciated, thanks.
If your StoredExercise model looks like this
class StoredExercise: Object {
#objc dynamic var muscle = ""
}
then to get all of the exercises that are for the biceps, it's this
let bicepResults = realm.objects(StoredExercise.self).filter("muscle == 'biceps'")

Designing a UITableView/Cell - iOS

I'm designing a UITableView using subviews to populate the reusable cell of it, and I wish some opinion about that.
As I had tested, it works well. But, I don't know if it is a good solution.
The scenario is: I have a tableview with different kind of cells (layouts). When I was designing, it grows fast (my controller code), as I had to register a lot of cell and handle cellForRow. Then I come with that idea, to instantiate different subviews for one unique reusable cell and use a 'Presenter' to handle delegate/datasource. You think is that a problem? And is that a good approach?
Thanks in advance!
Ps.: sorry for any english error!
EDITED:
Here is the session in project followed by de codes:
Codes at:
OrderDetailCell
class OrderDetailCell: UITableViewCell {
//MARK: Outlets
#IBOutlet weak var cellHeight: NSLayoutConstraint!
#IBOutlet weak var viewContent: UIView!
//Variables
var didUpdateLayout = false
internal func setupLayoutWith(view: UIView){
cellHeight.constant = view.frame.height
viewContent.frame = view.frame
viewContent.addSubview(view)
updateConstraints()
layoutIfNeeded()
didUpdateLayout = true
}
}
OrderDetailSubview
class OrderDetailSubview: UIView {
var type: OrderDetailsSubViewType?
var height: CGFloat = 1
class func instanceFromNib(withType type: OrderDetailsSubViewType) -> OrderDetailSubview {
let view = UINib(nibName: type.rawValue, bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! OrderDetailSubview
switch type {
case .OrderDetailSubviewStatus:
view.height = 258
case .OrderDetailSubViewItem:
view.height = 129
case .OrderDetailSubViewStoreInformation:
view.height = 317
case .OrderDetailSubViewEvaluation:
view.height = 150
}
view.updateConstraints()
view.layoutIfNeeded()
return view
}
}
OrderDetailPresenter
enum OrderDetailsSubViewType: String {
case OrderDetailSubviewStatus = "OrderDetailSubviewStatus",
OrderDetailSubViewItem = "OrderDetailSubViewItem",
OrderDetailSubViewStoreInformation = "OrderDetailSubViewStoreInformation",
OrderDetailSubViewEvaluation = "OrderDetailSubViewEvaluation"
static let types = [OrderDetailSubviewStatus, OrderDetailSubViewItem, OrderDetailSubViewStoreInformation, OrderDetailSubViewEvaluation]
}
class OrderDetailPresenter {
//Constants
let numberOfSections = 4
//Variables
// var order: Order?
func setup(reusableCell: UITableViewCell, forRowInSection section: Int) -> OrderDetailCell {
let cell = reusableCell as! OrderDetailCell
for sub in cell.viewContent.subviews {
sub.removeFromSuperview()
}
let subView = OrderDetailSubview.instanceFromNib(withType: OrderDetailsSubViewType.types[section])
cell.setupLayoutWith(view: subView)
return cell
}
func numberOfRowsForSection(_ section: Int) -> Int {
switch section {
case 1:
//TODO: count de offerList
return 4
default:
return 1
}
}
}
OrderDetailViewController
class OrderDetailViewController: BaseViewController {
//MARK: Outlets
#IBOutlet weak var tableView: UITableView!
var presenter = OrderDetailPresenter()
override func setupView() {
setupTableView()
}
}
extension OrderDetailViewController: UITableViewDataSource, UITableViewDelegate {
internal func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 600
tableView.rowHeight = UITableViewAutomaticDimension
tableView.register(UINib(nibName: "OrderDetailCell", bundle: nil), forCellReuseIdentifier: "OrderDetailCell")
}
func numberOfSections(in tableView: UITableView) -> Int {
return presenter.numberOfSections
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter.numberOfRowsForSection(section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reusableCell = tableView.dequeueReusableCell(withIdentifier: "OrderDetailCell") as! OrderDetailCell
let cell = presenter.setup(reusableCell: reusableCell, forRowInSection: indexPath.section)
return cell
}
}
*Sorry for indentation here...
Thats it! What you think?
Here you want to have multiple UITableViewCell subclasses that implement the different layouts that you want, and then select the relevant one in you table view data source.
class Cell1: UITableViewCell {
let label = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(label)
}
... whatever other setup/layout you need to do in the class ...
}
class Cell2: UITableViewCell {
let imageView = UIImageView()
override init(style: UITableViewCellStyle, reuseIdentifier: String) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(imageView)
}
... whatever other setup/layout you need to do in the class ...
}
Then in your view controller
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(Cell1.self, forCellReuseIdentifier: "cell1Identifier")
tableView.register(Cell2.self, forCellReuseIdentifier: "cell2Identifier")
}
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 { // just alternating rows for example
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1Identifier", for: indexPath) as! Cell1
// set data on cell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2Identifier", for: indexPath) as! Cell2
// set data on cell
return cell
}
}
So this is just an example, but is using two different cell subclasses for alternating rows in the table view.
let dynamicCellID: String = "dynamicCellID" //One Cell ID for resuse
class dynamicCell: UITableViewCell {
var sub: UIView // you just need to specify the subview
init(sub: UIView) {
self.sub = sub
super.init(style: .default, reuseIdentifier: dynamicCellID)
self.addSubview(sub)
self.sub.frame = CGRect(x: 0, y: 0, width: sub.frame.width, height: sub.frame.height)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And you need to create a views array the give that view to every cell in delegate
let views: [UIView] = []
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return views.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let v = views[indexPath.row]
return dynamicCell(sub: v)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let v = views[indexPath.row]
return v.frame.height + 10 //offset is 10 point
}

Each Cell need to have a Section - Parse and Swift

I'm implementing a Feed on my App using Parse.com, basically I'm populating a UITableViewController and everything works fine, BUT, I really like the way Instagram does, seems like the Instagram have a UIView inside each cell that works like a header and that view follows the scroll till the end of cell, I tried to search about that and I'm not successful, after some research I've realized that this feature is equally a Section, so I decide to implement Sections in my querys, I've implemented the code below:
import UIKit
class FeedTableViewController: PFQueryTableViewController {
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadCollectionViewData()
}
func loadCollectionViewData() {
// Build a parse query object
let query = PFQuery(className:"Feed")
// Check to see if there is a search term
// Fetch data from the parse platform
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
// The find succeeded now rocess the found objects into the countries array
if error == nil {
print(objects!.count)
// reload our data into the collection view
} else {
// Log details of the failure
}
}
}
// Initialise the PFQueryTable tableview
override init(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
// Configure the PFQueryTableView
self.parseClassName = "Feed"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return objects!.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section \(section)"
}
//override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! FeedTableViewCell!
if cell == nil {
cell = FeedTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell.anuncerPhoto.layer.cornerRadius = cell.anuncerPhoto.frame.size.width / 2
cell.anuncerPhoto.clipsToBounds = true
// Extract values from the PFObject to display in the table cell
if let nameEnglish = object?["name"] as? String {
cell?.title?.text = nameEnglish
}
let thumbnail = object?["Photo"] as! PFFile
let initialThumbnail = UIImage(named: "loadingImage")
cell.photoImage.image = initialThumbnail
cell.photoImage.file = thumbnail
cell.photoImage.loadInBackground()
return cell
}
}
Basically I will need to have a section for each cell, Now I'm successfully have sections working for each cell, but the problem is that the querys is repeating on the first post.
In the backend I have 3 different posts, so, in the App the UItableview need to have 3 posts with different content, with the code above I'm successfully counting the number of posts to know how many section I'll need to have and I declare that I want one post per section, but the app shows 3 sections with the same first post.
Any ideas if I'm capture the correct concept of the Instagram feature and why I'm facing this problem in my querys?
Thanks.
Keep the original UITableViewDataSource method and retrieve the current object using the indexPath.section
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! FeedTableViewCell!
if cell == nil {
cell = FeedTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
cell.anuncerPhoto.layer.cornerRadius = cell.anuncerPhoto.frame.size.width / 2
cell.anuncerPhoto.clipsToBounds = true
let object = objects[indexPath.section]
// Extract values from the PFObject to display in the table cell
if let nameEnglish = object["name"] as? String {
cell?.title?.text = nameEnglish
}
let thumbnail = object["Photo"] as! PFFile
let initialThumbnail = UIImage(named: "loadingImage")
cell.photoImage.image = initialThumbnail
cell.photoImage.file = thumbnail
cell.photoImage.loadInBackground()
return cell
}

Trying to display data on a UITableView from parse

I'm trying to display data from parse onto a UITableView but it's only displaying a blank UITableView (no data being shown)
I have a University class in parse, as well as a universityEnrolledName column name
here is the code
import UIKit
import Parse
import ParseUI
class viewUniversityList: PFQueryTableViewController {
#IBOutlet var uiTableView: UITableView!
override init(style: UITableViewStyle, className: String!){
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
self.parseClassName = "University"
self.textKey = "universityEnrolledName"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
override func queryForTable() -> PFQuery {
var query = PFQuery(className: "University")
query.orderByAscending("universityEnrolledName")
return query;
}
override func viewDidLoad() {
super.viewDidLoad()
uiTableView.delegate = self
uiTableView.dataSource = self
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! PFTableViewCell!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
if let universityEnrolledName = object?["universityEnrolledName"] as? String{
cell?.textLabel?.text = universityEnrolledName
}
if let classEnrolledName = object?["classEnrolledName"] as? String{
cell?.detailTextLabel?.text = classEnrolledName
}
return cell;
}
/*
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
}
does anyone have any advice on displaying the universityEnrolledName data from the University class (from Parse)? Thanks!
Here,
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return 0
}
This method is used to display number rows in a tableview. Since you are returning 0, which means you are telling to your table view that your table should have zero row's.
Similarly
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 0
}
By default table view have one section, if you are explicitly providing some value it will be overridden.
So in both case you should return some positive number greater than zero.
The below method should return UITableViewCell, but you wrote PFTableViewCell. So change it.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! PFTableViewCell!
if cell == nil {
cell = PFTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
if let universityEnrolledName = object?["universityEnrolledName"] as? String{
cell?.textLabel?.text = universityEnrolledName
}
if let classEnrolledName = object?["classEnrolledName"] as? String{
cell?.detailTextLabel?.text = classEnrolledName
}
return cell;
}
override func viewDidLoad() {
super.viewDidLoad()
//Conform to the TableView Delegate and DataSource protocols
uiTableView.delegate = self //set delegate
uiTableView.dataSource = self // set datasource
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 0 //set count of section
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0 //set count of rows
}
Refere this:
how-to-make-a-simple-table-view-with-ios-8-and-swift
This might helps you :)
I don't write swift so forgive any syntax issues, but I expect you want something more like:
import UIKit
import Parse
import ParseUI
class viewUniversityList: PFQueryTableViewController {
#IBOutlet var uiTableView: UITableView!
override init(style: UITableViewStyle, className: String!){
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
self.parseClassName = "University"
self.textKey = "universityEnrolledName"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
override func viewDidLoad() {
super.viewDidLoad()
uiTableView.delegate = self
uiTableView.dataSource = self
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func queryForTable() -> PFQuery {
var query = PFQuery(className: "University")
query.orderByAscending("universityEnrolledName")
return query;
}
override func textKey() -> NSString {
return "universityEnrolledName"
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = super.tableView(tableView, dequeueReusableCellWithIdentifier:indexPath, object:object) as! PFTableViewCell!
if let classEnrolledName = object?["classEnrolledName"] as? String{
cell?.detailTextLabel?.text = classEnrolledName
}
return cell;
}

PFQueryTableViewController doesn't work with iOS 8 feature of autoresizing cells?

I have a UIViewController with table view which takes the data from Parse and it works well with auto sizing. While reading the documentation I saw that there is a controller named PFQueryTableViewController which comes whit pagination, pull to refresh and other cool features. But now the auto sizing doesn't work and every cell covers the next. Here's my code:
import UIKit
class WallParse: PFQueryTableViewController {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.parseClassName = "Post"
self.pullToRefreshEnabled = true
self.paginationEnabled = true
self.objectsPerPage = 3
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 400
self.tableView.rowHeight = UITableViewAutomaticDimension
}
override init!(style: UITableViewStyle, className: String!) {
super.init(style: style, className: className)
}
override func queryForTable() -> PFQuery {
var query = PFQuery (className: self.parseClassName)
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
query.orderByDescending("createdAt")
return query
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!, object: PFObject!) -> PFTableViewCell! {
let cellIdentifier = "cell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as WallCell
// Configure the cell to show todo item with a priority at the bottom
cell.commentLabel.text = object["text"] as? String
return cell;
}
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
}

Resources