UITableView not populated. Item size is 2 - ios

I'm in the process of making a small app for myself to try out Swift and so forth, and I think I am missing something simple here.
So in my controller, i have the proper delegates and methods to support it:
class MatchListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
I then setup & populate the table
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
loadResults()
}
func loadResults() {
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext! = delegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Series")
var error:NSError? = nil
results = context.executeFetchRequest(fetchRequest, error: &error) as NSArray
}
In Storyboard, the UITableView is connected to my IBOutlet, and refers to MatchListViewController as its delegate.
#IBOutlet var tableView: UITableView!
I setup the cellForRowAtIndexPath method as such:
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
if let row = indexPath?.row {
let object = results.objectAtIndex(row) as NSObject
if (object != nil) {
let teamOneName = object.valueForKey("team_one") as NSString
let teamTwoName = object.valueForKey("team_two") as NSString
cell.textLabel!.text = "\(teamOneName) vs \(teamTwoName)"
}
}
return cell
}
Any ideas on what I may be missing here?

Related

How to update DetailView

I have a swift app based on Master-Detail template. Every row in MasterView table is based on custom cell received from a nib. Every cell includes UIlabel and UIbutton. The logic of the app is following. If user taps on a row DetailView shows some details depending on selected row. The button on the row does not call tableView(_, didSelectRowAtIndexPath). If user taps on the button inside a row only an image belongs to DetailView should be changed (other elements on DetailView remain the same) but it isn't. If I select another row and than select previous row back, changed image is shown on the DetailView as it was foreseen. The question is how to redraw the image in the DetailView just by tapping on the button.
I've tried to do following but with no success:
class MasterViewCell: UITableViewCell {
weak var detailViewController: DetailViewController?
#IBAction func buttonTap(sender: AnyObject) {
//method to set new image
detailViewController!.setNewImage()
detailViewController!.view.setNeedsDisplay()
}
}
class MasterViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "itemCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "Cell")
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? MasterViewCell
cell?.detailView = self.detailViewController
return cell!
}
You need to use a handler
typealias ButtonHandler = (Cell) -> Void
class Cell: UITableViewCell {
var changeImage: ButtonHandler?
func configureButton(changeImage: ButtonHandler?) {
self.changeImage = changeImage
}
#IBAction func buttonTap(sender: UIButton) {
changeImage?(self)
}
}
And in your MasterView
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! Cell
cell.configureButton(setNewImage())
return cell
}
private func setNewImage() -> ButtonHandler {
return { [unowned self] cell in
let row = self.tableView.indexPathForCell(cell)?.row //Get the row that was touched
//set the new Image
}
}
SOURCE: iOS Swift, Update UITableView custom cell label outside of tableview CellForRow using tag
I've found the solution. I've used protocol-delegate mechanism. Now the code is:
//protocol declaration:
protocol MasterViewCellDelegate: class {
func updateImage(sender: MasterViewCell, detVC: DetailViewController)
}
// cell class
class MasterViewCell: UITableViewCell {
weak var masterViewCellDelegate: MasterViewCellDelegate? // protocol property
weak var masterViewController: MasterViewController? {
didSet {
// set delegate
self.masterViewDelegate = masterViewController!.detailViewController
}
}
#IBAction func buttonTap(sender: AnyObject) {
var detVC: DetailViewController?
if let split = masterViewController!.splitViewController {
let controllers = split.viewControllers
detVC = (controllers[controllers.count - 1] as! UINavigationController).topViewController as? DetailViewController
}
// call delegate
masterViewCellDelegate?.updateImage(self, detVC: detVC)
}
class MasterViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib(nibName: "itemCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "Cell")
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? MasterViewCell
cell?.masterViewController = self
return cell!
}
// declare detailviewcontroller as delegate
class DetailViewController: UIViewController, MasterViewCellDelegate {
func updateImage(sender: MasterViewCell, detVC: DetailViewController){
detVC.setNewImage()
}
}
It may well be that this solution is excessively complex, but it works and easy could be adapted for various purposes.

How to set CustomTableView immediately (CoreData)

I have a TextField, Button and a TableView in ViewController
I press Button -> import Text to Data and also export to TableView
But it does not work
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
myTableView.reloadData()
}
#IBOutlet weak var txtClient: UITextField!
#IBAction func butNhap(sender: AnyObject) {
var newName = txtClient.text as String
var myDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var myContext: NSManagedObjectContext = myDelegate.managedObjectContext!
var myText: AnyObject = NSEntityDescription.insertNewObjectForEntityForName("Client", inManagedObjectContext: myContext)
myText.setValue(newName, forKey: "name")
txtClient.text = ""
}
#IBOutlet weak var myTableView: UITableView!
var exportArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.delegate = self
self.myTableView.dataSource = self
myTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "myCell")
}
func export() -> [String] {
var myDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var myContext: NSManagedObjectContext = myDelegate.managedObjectContext!
var exportName = NSFetchRequest(entityName: "Client")
exportName.returnsObjectsAsFaults = false
var exportValue = myContext.executeFetchRequest(exportName, error: nil)
for result: AnyObject in exportValue! {
exportArray.insert((result.valueForKey("name") as! String), atIndex: 0)
}
return exportArray
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = exportArray[indexPath.row]
return cell
}
}
Create one custom UITableviewcell and give name and identifier it like "customtableCell", then on the respective view controller you have to register that custom cell,
let nibName = UINib(nibName: "customtableCell", bundle:nil)
tblConnect!.registerNib(nibName, forCellReuseIdentifier: "customtableCell")
After that on the cellForRowAtIndexPath use below type of code,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let identifier = "customtableCell"
var tablecell: customtableCell!
if (tablecell == nil) {
tablecell = tableView.dequeueReusableCellWithIdentifier(identifier) as? customtableCell
}
return tablecell
}

Read & Save setting an array in array in swift

I try to load this array out from my save setting "Mybookmarks", and try to write it in a table. That is in the setting =>
[["name" : "apple", "url": "http://www.apple.de"],
["name" : "youtube", "url": "http://www.youtube.de"]]
But it doesn't work, it said always crash. I want to load this kind of array and write it in a tableview.
Any idea on how to fix this so that it can read this from my "var defaults = NSUserDefaults.standardUserDefaults()".
// ViewController.swift
import UIKit
class ViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
// create references to the items on the storyboard
// so that we can animate their properties
var tableView: UITableView = UITableView()
var objects = []
override func viewDidLoad() {
super.viewDidLoad()
var defaults = NSUserDefaults.standardUserDefaults()
//read
if let testArray : AnyObject? = defaults.objectForKey("Mybookmarks") {
var objects : [NSString] = testArray! as! [NSString]
println("\(testArray)")
}
tableView.frame = CGRectMake(0, 0, 300, view.frame.height);
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(tableView)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.objects.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
let object = self.objects[indexPath.row]
cell.textLabel?.text = object["name"]!
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
}
}
the type of the array is an array of dictionaries [[String:String]] rather than array of strings [String]
var objects = [[String:String]]()
...
if let testArray : AnyObject? = defaults.objectForKey("Mybookmarks") {
objects = testArray as! [[String:String]]
}
Edit:
and connect the tableview instance in Interface Builder with an IBOutlet.
The instance you're creating with var tableView: UITableView = UITableView() is a brand new instance, it's different from those in IB
nevermind I see that this is not in [String:String]. This must be in let object:
[String:String] = self.objects[indexPath.row]
and now it works

Swift - Retrieving core data to custom cell

I'm trying to retrieve core data and display it in a custom cell class. I think it will be easier if I present my code first.
This is my "original code", with a regular cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: UITableViewCell = self.tv.dequeueReusableCellWithIdentifier(CellID) as UITableViewCell
if let ip = indexPath as Optional {
var data:NSManagedObject = myList[ip.row] as NSManagedObject
cell.textLabel!.text = data.valueForKeyPath("username") as String!
}
return cell
}
This is what I change my code to when trying to use the custom cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: CustomCell = tv.dequeueReusableCellWithIdentifier(CellID) as CustomCell
if let ip = indexPath as Optional {
var data:NSManagedObject = myList[ip.row] as NSManagedObject
cell.titleLabel.text = data.valueForKeyPath("username") as String!
cell.dateLabel.text = data.valueForKeyPath("date") as String!
}
return cell
}
The first code works perfectly, but when using the second one I get the (lldb) runtime error.
Both "username" and "date" are saved as strings.
Any suggestions would be appreciated.
EDIT:
Additional information:
var myList: Array<AnyObject> = []
The error that pops up is just "(lldb)" and "Thread 1: EXC_BREAKPOINT (code = EXC_l386_BPT, subcode = 0x0)".
My model-file:
#objc(Model)
class Model: NSManagedObject {
#NSManaged var username: String
#NSManaged var date: String
#NSManaged var isAnonymousMessage: Bool
}
My cellForRowAtIndexPath-function:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellID:NSString = "cell"
var cell: CustomCell = tv.dequeueReusableCellWithIdentifier(CellID) as CustomCell
if let ip = indexPath as Optional {
let data = myList[indexPath.row] as Model
cell.titleLabel.text = data.username
cell.dateLabel.text = data.date
}
return cell
}
My viewDidAppear-function:
override func viewDidAppear(animated: Bool) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "Message")
let en = NSEntityDescription.entityForName("Message", inManagedObjectContext: context)
let fetchRequest = NSFetchRequest(entityName: "Message")
myList = context.executeFetchRequest(fetchRequest, error: nil) as [Model]
tv.reloadData()
}
my CustomCell class looks like this:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I would suggest to change your code a little bit.
Load your NSManagedObjects into an Array of your Core Data Class, for example like this:
var myList = [ListObject]() // Where ListObject is your NSManagedClass
let fetchRequest = NSFetchRequest(entityName: "List")
myList = context.executeFetchRequest(fetchRequest, error: nil) as [ListObject]
// You should load this once (maybe in ViewDidLoad) so every Core Data object gets only fetched once (you could easy refresh this if needed).
Then use in your cellForRowAtIndexPath:
let data = myList[indexPath.row] as ListObject
cell.titleLabel.text = data.name
cell.dateLabel.text = data.date
// You dont need to use "valueForKeyPath" - just use the property as shown above.
var cell: CustomCell = tv.dequeueReusableCellWithIdentifier(CellID) as CustomCell
Crashing on this line means that you're not getting a CustomCell back when you dequeue from the tableview.
You need to register the class with that reuse identifier, either by setting it in the storyboard or xib, or calling registerClass(_ cellClass: AnyClass, forCellReuseIdentifier identifier: String) on the table view, normally in view did load.
If you've added a new cell to the storyboard and want to use it instead of the default one, make sure the reuse identifier is set correctly.

ios, swift, core data + multiple tables

I'm very new to ios and swift. On a single view, how can I send two different fetch requests to two different table views? I have a class level fetchReq function that uses NSPredicate to take a parameter and give me the varied results that I want. The only place that knows which table is which is the tablView func, but it looks like the decision about which data to load gets made immediately on viewDidLoad. Could some kind soul help me restructure the core data code so that I get a different fetch request for each table?
import UIKit
import CoreData
class CustomTableViewCell : UITableViewCell {
#IBOutlet var l1: UILabel?
#IBOutlet var l2: UILabel?
func loadItem(#number: String, name: String) {
l1!.text = number
l2!.text = name
}
}
class ViewController: UIViewController, UITableViewDelegate, NSFetchedResultsControllerDelegate, UITableViewDataSource {
#IBOutlet var tableView1: UITableView!
//this is my second table - Ive connected it in the IB to this VC. both tables work, but are identical
#IBOutlet var tableView2: UITableView!
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()
//the filtering happens inside this function. it gets called via didLoad, not cellsForRows
func playerFetchRequest(playerType: String) -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Players")
let sortDescriptor = NSSortDescriptor(key: "number", ascending: true)
let filter = NSPredicate(format: "%K = %#", "type", playerType)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = filter
return fetchRequest
}
func getFetchedResultController() -> NSFetchedResultsController {
fetchedResultController = NSFetchedResultsController(fetchRequest: playerFetchRequest(playerType), managedObjectContext:managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultController
}
//remember: to create a table with multiple sections just implement the numberOfSectionsInTableView(_:) method
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let numberOfRowsInSection = fetchedResultController.sections?[section].numberOfObjects
{return numberOfRowsInSection} else {return 0}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if (tableView == tableView2) {
var playerType = "Forward"
var cell:CustomTableViewCell = self.tableView1.dequeueReusableCellWithIdentifier("customCell") as CustomTableViewCell
let player = fetchedResultController.objectAtIndexPath(indexPath) as DataModel
cell.l2?.text = player.lastName + ", " + player.firstName
cell.l1?.text = player.number
println(tableView)
return cell
}
else {
var playerType = "Defender"
var cell:CustomTableViewCell = self.tableView2.dequeueReusableCellWithIdentifier("customCell") as CustomTableViewCell
let player = fetchedResultController.objectAtIndexPath(indexPath) as DataModel
cell.l2?.text = player.lastName + ", " + player.firstName
cell.l1?.text = player.number
println(tableView)
return cell
}
}
func tableView(tableView: UITableView!, didDeselectRowAtIndexPath indexPath: NSIndexPath!) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
println("You selected cell #\(indexPath.row)!")
}
override func viewDidLoad() {
var nib = UINib(nibName: "CustomTableViewCell", bundle: nil)
tableView1.registerNib(nib, forCellReuseIdentifier: "customCell")
tableView2.registerNib(nib, forCellReuseIdentifier: "customCell")
fetchedResultController = getFetchedResultController()
fetchedResultController.delegate = self
fetchedResultController.performFetch(nil)
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func controllerDidChangeContent(controller: NSFetchedResultsController!) {
tableView1.reloadData()
tableView2.reloadData()
}
}
You need 2 fetchedResultsControllers with the two different fetch requests for each table. If your tables delegates and datasources are both this view controller, you'll need to switch and provide the corresponding content... for example:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (tableView == tableView2)
{
return fetchedResultController2.sections?[section].numberOfObjects
}
else
{
return fetchedResultController.sections?[section].numberOfObjects
}
}
Another option would be to create 2 custom MYTableViewDataSource objects and set the datasource for each table view to that... It might make it more obvious when you've got unexpected behaviour and make the data easier to control.
Just establish two separate NSFetchedResultsController objects, one for each table:
var forwardFetchedResultController: NSFetchedResultsController
var defenderFetchedResultController: NSFetchedResultsController
then in viewDidLoad create them with different NSFetchRequests for each. And in your tableView functions, use the correct fetched results controller for the correct table.

Resources