How to create a NSTableview with custom viewcells - cocoa

I tried many different ways to create a NSTableview with custom NSTableCellView but I could not make it work. The question is, how can I do that thing?
Here is the last thing I tried:
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
var cellIdentifier: String = ""
if tableColumn == tableView.tableColumns[0] {
cellIdentifier = "CellID"
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
cell.identifier = cellIdentifier
// array is an array that contains NSView with layers with different colors
cell.myView = array[row]
return cell
}
}
return nil
}
After adding a label:
And the full code:
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
tableview.setDelegate(self)
tableview.setDataSource(self)
let view = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))
view.layer?.backgroundColor = NSColor.blueColor().CGColor
array.append(view)
let view2 = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))
view2.layer?.backgroundColor = NSColor.greenColor().CGColor
array.append(view2)
array2label.append("bu")
array2label.append("buu")
tableview.reloadData()
// Do any additional setup after loading the view.
}
override func viewWillAppear() {
//tableview.reloadData()
laView.layer?.backgroundColor = NSColor.greenColor().CGColor
}
#IBOutlet weak var laView: NSView!
#IBOutlet weak var tableview: NSTableView!
var array = [NSView]()
var array2label = [String]()// = ["bu","buu"]
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
if (tableView.identifier == "Taula") {
return array.count
//return taulaGrafics.count
} else {
return 0
}
}
#IBOutlet weak var viewDeProva: NSView!
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
print ( "Preparem la TableView" )
var cellIdentifier: String = ""
if tableColumn == tableView.tableColumns[0] {
cellIdentifier = "CellID"
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
print ( "aqui" )
print(array)
print(array2label)
cell.identifier = cellIdentifier
cell.myView = array[row]
cell.label.stringValue = array2label[row]
return cell
}
}
return nil
}
#IBAction func afegir(sender: NSButton) {
let view = NSView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))
view.layer?.backgroundColor = NSColor.yellowColor().CGColor
array.append(view)
array2label.append("buLabel")
tableview.reloadData()
}
#IBAction func treure(sender: NSButton) {
array.removeLast()
tableview.reloadData()
}
}
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
print ( "aqui" )
print(array)
print(array2label)
cell.identifier = cellIdentifier
cell.myView = array[row]
cell.myView.wantsLayer = true
cell.label.stringValue = array2label[row]
return cell
}

With this changes the view show the color that is expected to be:
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
print("DidLoad")
tableview.setDelegate(self)
tableview.setDataSource(self)
}
override func viewWillAppear() {
print("WillAppear")
array.append(NSColor.blueColor().CGColor)
array2label.append("buIni")
tableview.reloadData()
laView.layer?.backgroundColor = NSColor.greenColor().CGColor
}
#IBOutlet weak var laView: NSView!
#IBOutlet weak var tableview: NSTableView!
var array = [CGColor]()
var array2label = [String]()
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
if (tableView.identifier == "Taula") {
return array.count
//return taulaGrafics.count
} else {
return 0
}
}
#IBOutlet weak var viewDeProva: NSView!
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
print ( "Preparem la TableView" )
var cellIdentifier: String = ""
if tableColumn == tableView.tableColumns[0] {
cellIdentifier = "CellID"
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
print ( "Here" )
print(array)
print(array2label)
cell.identifier = cellIdentifier
cell.myView.layer?.backgroundColor = array[row]
cell.label.stringValue = array2label[row]
return cell
}
}
return nil
}
#IBAction func afegir(sender: NSButton) {
let color = NSColor.yellowColor().CGColor
array.append(color)
array2label.append("buLabel")
tableview.reloadData()
}
#IBAction func treure(sender: NSButton) {
array.removeLast()
array2label.removeLast()
tableview.reloadData()
}
}
The problem was that the NSView inside the NSTableViewCell can't be replaced by another view, because what you do doing that is changing the prototype cell. So if you replace the view the NSTableViewCell doesn't recognize that NewView layer (I'm not sure 100% why). Looks like just some information is shared.
So, how can we pass info to the tableview cell? How can we show a layer there?? Well the answer is that just modifying the prototype cell NSTableCellView added on the interface builder or its subclass (like my case, see bellow the cell). Modifying its content, but not the NSView!, inside the viewForTableColumn function. See in this piece of code:
if let cell = tableView.makeViewWithIdentifier(cellIdentifier, owner: self ) as? MyTableCellView {
cell.identifier = cellIdentifier // that is to identify the cell
cell.myView.layer?.backgroundColor = array[row] // see that the array contains CGCOLORS not NSViews
// The program was not showing the colors because the
// view copied doesn't copy its layer, or at least
// doesn't show it.
// Even though, if you say: *******---This is incorrect:
cell.myView = arrayOfNSViews[row] //the program will
// crash when removing 2 rows, myView = nil !!!!.
// That is because when you remove the item of the array
// somehow you are removing myView too, because it make a
// copy or a reference to it (Not sure what exactly).
// Here continues the correct program: ******---
cell.label.stringValue = array2label[row]
return cell
}
Also see that in the textField case: cell.label.stringValue = array2label[row], you change the string value of the textfield, not the whole NSTextfield.
So guys remember and repeat my words: "I'm not going to change the view of the cell, just its properties". I just spend 4 days to find that...
Here is the NStableCellView promised:
class MyTableCellView: NSTableCellView {
#IBOutlet weak var myView: NSView!
#IBOutlet weak var label: NSTextField!
}
One Image of the view hierarchy:

Related

Swift 3 Table Column Width Last Column Not Working

Refer to my attached image.
Notice the last column for some reason is always short on the width. I can't for the life of me figure out why or how to fix this?
Here is my code for my controller.
import Cocoa
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var theTableview: NSTableView!
var data:NSArray = [""] //#JA - This is used
override func viewDidLoad() {
super.viewDidLoad()
//First remove all columns
let columns = self.theTableview.tableColumns
columns.forEach {
self.theTableview.removeTableColumn($0)
}
//self.theTableview?.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
for index in 0...100 {
let column = NSTableColumn(identifier: "defaultheader")
if(index != 0){
column.title = "Month \(index)"
}else{
column.title = "Factors"
}
self.theTableview.addTableColumn(column)
}
// Do any additional setup after loading the view.
data = ["Group 1","Group 2","Group 3","Group 4"]
self.theTableview.reloadData()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func numberOfRows(in tableView: NSTableView) -> Int {
return data.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let cell = tableView.make(withIdentifier: "defaultcell", owner: nil) as? NSTableCellView {
cell.textField?.stringValue = data.object(at: row) as! String
return cell
}
return nil
}
#IBAction func startsimulation(_ sender: NSButton) {
//Recalculates the data variable for updating the table.
data = ["group1","group2"]
theTableview.reloadData()
}
}
NSTableColumn has a property resizingMask and NSTableView has a property columnAutoresizingStyle. Both can be set in IB or in code. Figure out a configuration so the columns behave like you want. The default Column Sizing of the table view in IB is 'Last Column Only', switching to 'None' will fix your problem.
Another solution is setting minWidth of the columns.

Why pushViewController doesn't work in UIPageViewController?

I use uipageViewController for sliding pages. Each page has own ViewController:ContentViewController. In one of page I have button. When user click on this button it should go to another ViewController (let say home page). When I click on the button it change state, but redirection doesn't work. It seems that smth wrong with pushViewController?
Here is my code ::
class ViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController : UIPageViewController!
var pageTitles : NSArray!
var pageImages : NSArray!
let controllers = ["FirstViewController","SecondViewController"]
#IBOutlet weak var restartButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
self.pageTitles = NSArray(objects: "Explore", "Today Widget")
self.pageImages = NSArray(objects: "page1","page2")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController")as! UIPageViewController
self.pageViewController.dataSource = self
var startVC = self.viewControllerAtIndex(0)as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as! [UIViewController], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0,40,self.view.frame.width,self.view.frame.height - 60)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
self.view.backgroundColor = UIColor(red: (25/255.0), green: (127/255.0), blue: (216/255.0), alpha: 1.0)
// 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.
}
#IBAction func restartAction(sender: AnyObject) {
var startVC = self.viewControllerAtIndex(0)as ContentViewController
var viewControllers = NSArray (object: startVC)
self.pageViewController.setViewControllers(viewControllers as! [UIViewController], direction: .Forward, animated: true, completion: nil)
}
func viewControllerAtIndex(index : Int)->ContentViewController
{
if (self.pageTitles.count == 0) || (index >= self.pageTitles.count){
return ContentViewController();
}
var vc:ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController")as! ContentViewController
vc = self.storyboard?.instantiateViewControllerWithIdentifier(controllers[index])as! ContentViewController
vc.pageIndex = index
return vc;
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0 || index == NSNotFound){
return nil
}
index--
return self.viewControllerAtIndex(index);
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound){
return nil
}
index++
if (index == self.pageTitles.count){
return nil
}
return self.viewControllerAtIndex(index)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return self.pageTitles.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
#IBAction func secondViewController(sender: AnyObject) {
let next = self.storyboard?.instantiateViewControllerWithIdentifier("ThirdViewController") as? ThirdViewController
navigationController?.pushViewController(next!,animated: true)
}

Terminator found in the middle of a basic block

All went fine until my project won't compile.I see those things on two of my files.
Terminator found in the middle of a basic block!
label %50
LLVM ERROR: Broken function found, compilation aborted!
Terminator found in the middle of a basic block!
label %71
LLVM ERROR: Broken function found, compilation aborted!
And the error the compiler give
Command /Applications/Xcode-beta 2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1
I tried solving it, but no success.
Older version of the files compile.
The problem is in the files,but what is the terminator?
Where is the problem?
EDIT
Here is some code, the classes are big
LandscapeViewController
import UIKit
class LandscapeViewController: UIViewController,UIScrollViewDelegate {
// MARK: Properties
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
var search: Search!
private var firstTime = true
private var downloadTasks = [NSURLSessionDownloadTask]()
#IBAction func pageChange(sender: UIPageControl){
UIView.animateWithDuration(0.3, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
self.scrollView.contentOffset = CGPoint(x: self.scrollView.bounds.size.width * CGFloat(sender.currentPage), y: 0)
}, completion: nil )
}
// MARK: Buttons
private func tileButtons(searchResults: [SearchResult]){
var columnsPerPage = 5
var rowsPerPage = 3
var itemWidth: CGFloat = 96
var itemHeight: CGFloat = 88
var marginX: CGFloat = 0
var marginY: CGFloat = 20
let buttonWidth: CGFloat = 82
let buttonHeight: CGFloat = 82
let scrollViewWidth = scrollView.bounds.size.width
switch scrollViewWidth{
case 568:
columnsPerPage = 6
itemWidth = 94
marginX = 2
case 667:
columnsPerPage = 7
itemWidth = 95
itemHeight = 98
marginX = 1
marginY = 29
case 736:
columnsPerPage = 8
rowsPerPage = 4
itemWidth = 92
default:
break
}
let paddingHorz = (itemWidth - buttonWidth)/2
let paddingVert = (itemHeight - buttonHeight)/2
var row = 0
var column = 0
var x = marginX
for (index,searchResult) in searchResults.enumerate(){
let button = UIButton(type: .Custom)
button.setBackgroundImage(UIImage(named: "LandscapeButton"), forState: .Normal)
downloadImageForSearchResult(searchResult, andPlaceOnButton: button)
button.tag = 2000 + index
button.addTarget(self, action: Selector("buttonPressed:"), forControlEvents: .TouchUpInside)
button.backgroundColor = UIColor.whiteColor()
button.frame = CGRect(x: x + paddingHorz, y: marginY + CGFloat(row)*itemHeight + paddingVert, width: buttonWidth, height: buttonHeight)
scrollView.addSubview(button)
++row
if row == rowsPerPage{
row = 0
++column
x += itemWidth
if column == columnsPerPage{
column = 0
x += marginX * 2
}
}
}
let buttonsPerPage = columnsPerPage * rowsPerPage
let numPages = 1 + (searchResults.count - 1) / buttonsPerPage
pageControl.numberOfPages = numPages
pageControl.currentPage = 0
scrollView.contentSize = CGSize(width: CGFloat(numPages) * scrollViewWidth, height: scrollView.bounds.size.height)
}
private func downloadImageForSearchResult(searchResult: SearchResult,andPlaceOnButton button: UIButton){
if let url = NSURL(string: searchResult.artworkURL60){
let session = NSURLSession.sharedSession()
let downloadTask = session.downloadTaskWithURL(url,completionHandler: { [weak button] url, response, error in
if error == nil && url != nil{
if let data = NSData(contentsOfURL: url!){
if let image = UIImage(data: data){
let resizedImage = image.resizedImageWithBounds(CGSize(width: 60, height: 60))
dispatch_async(dispatch_get_main_queue()){
if let button = button{
button.setImage(resizedImage, forState: .Normal)
}
}
}
}
}
})
downloadTasks.append(downloadTask!)
downloadTask?.resume()
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail"{
switch search.state{
case .Results(let list):
let destinationViewController = segue.destinationViewController as! DetailViewController
let searchResult = list[sender!.tag - 2000]
destinationViewController.searchResult = searchResult
default:
break
}
}
}
func buttonPressed(sender: UIButton){
performSegueWithIdentifier("ShowDetail", sender: sender)
}
// MARK: UIScrollViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView) {
let width = scrollView.bounds.size.width
let currentPage = Int((scrollView.contentOffset.x + width/2) / width)
pageControl.currentPage = currentPage
}
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
pageControl.numberOfPages = 0
scrollView.backgroundColor = UIColor(patternImage: UIImage(named: "LandscapeBackground")!)
view.removeConstraints(view.constraints)
view.translatesAutoresizingMaskIntoConstraints = true
pageControl.removeConstraints(pageControl.constraints)
pageControl.translatesAutoresizingMaskIntoConstraints = true
scrollView.removeConstraints(scrollView.constraints)
scrollView.translatesAutoresizingMaskIntoConstraints = true
// Do any additional setup after loading the view.
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
scrollView.frame = view.bounds
pageControl.frame = CGRect(x: 0, y: view.frame.size.height - pageControl.frame.size.height, width: view.frame.size.width, height: pageControl.frame.size.height)
if firstTime{
firstTime = false
switch search.state{
case .NotSearchedYet:
break
case .Loading:
showSpinner()
case .NoResults:
showNothingFoundLabel()
case.Results(let list):
tileButtons(list)
}
}
}
private func showNothingFoundLabel() {
let label = UILabel(frame: CGRect.zeroRect)
label.text = "Nothing Found"
label.backgroundColor = UIColor.clearColor()
label.textColor = UIColor.whiteColor()
label.sizeToFit()
var rect = label.frame
rect.size.width = ceil(rect.size.width/2) * 2
rect.size.height = ceil(rect.size.height/2) * 2
label.frame = rect
label.center = CGPoint(x: CGRectGetMidX(scrollView.bounds), y: CGRectGetMidY(scrollView.bounds))
view.addSubview(label)
}
private func showSpinner(){
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
spinner.center = CGPoint(x: CGRectGetMidX(scrollView.bounds) + 0.5, y: CGRectGetMidY(scrollView.bounds) + 0.5)
spinner.tag = 1000
view.addSubview(spinner)
spinner.startAnimating()
}
func searchResultsRecived(){
hideSpinner()
switch search.state {
case .NotSearchedYet, .Loading:
break
case .NoResults:
showNothingFoundLabel()
case .Results(let list):
tileButtons(list)
}
}
private func hideSpinner(){
view.viewWithTag(1000)?.removeFromSuperview()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
deinit{
print("deinit \(self)")
for task in downloadTasks{
task.cancel()
}
}
/*
// 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.
}
*/
}
SearchViewController
import UIKit
class SearchViewController: UIViewController,UISearchBarDelegate,UITableViewDataSource,UITableViewDelegate {
private struct TableViewCellIdentifier {
static let searchResultCell = "SearchResultCell"
static let nothingFoundCell = "NothingFoundCell"
static let loadingCell = "LoadingCell"
}
// MARK: LandscapeViewController
override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)
switch newCollection.verticalSizeClass{
case .Compact:
showLandscapeViewWithCoordinator(coordinator)
case .Regular,.Unspecified:
hideLandscapeViewWithCoordinator(coordinator)
}
}
private func showLandscapeViewWithCoordinator(coordinator: UIViewControllerTransitionCoordinator){
precondition(landscapeViewController == nil)
landscapeViewController = storyboard?.instantiateViewControllerWithIdentifier("LandscapeViewController") as? LandscapeViewController
if let controller = landscapeViewController{
controller.search = search
controller.view.frame = view.frame
controller.view.alpha = 0
view.addSubview(controller.view)
addChildViewController(controller)
coordinator.animateAlongsideTransition({ _ in
if self.presentedViewController != nil{
self.dismissViewControllerAnimated(true, completion: nil)
}
self.searchBar.resignFirstResponder()
controller.view.alpha = 1
}, completion: { _ in
controller.didMoveToParentViewController(self)
})
}
}
private func hideLandscapeViewWithCoordinator(coordinator: UIViewControllerTransitionCoordinator){
if let controller = landscapeViewController{
controller.willMoveToParentViewController(nil)
coordinator.animateAlongsideTransition({ _ in
controller.view.alpha = 0
}, completion: { _ in
if self.presentedViewController != nil {
self.dismissViewControllerAnimated(true, completion: nil)
}
controller.view.removeFromSuperview()
controller.removeFromParentViewController()
self.landscapeViewController = nil
})
}
}
private func showNetworkError(){
let alert = UIAlertController(title: "Whoops...", message: "There was an error reading from the iTunes Store. Please try again.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(action)
presentViewController(alert, animated: true, completion: nil)
}
// MARK: Detail ViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetail" {
switch search.state{
case . Results(let list):
let indexPath = sender as! NSIndexPath
let searchResult = list[indexPath.row]
let detailViewController = segue.destinationViewController as! DetailViewController
detailViewController.searchResult = searchResult
default:
break
}
}
}
// MARK: Properties
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var segmentedControl: UISegmentedControl!
let search = Search()
private var landscapeViewController: LandscapeViewController?
// MARK: Methodes
#IBAction func segmentedControl(sender: UISegmentedControl) {
performSearch()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.contentInset = UIEdgeInsets(top: 108, left: 0, bottom: 0, right: 0)
tableView.rowHeight = 80
searchBar.becomeFirstResponder()
configureNib(nibName: TableViewCellIdentifier.searchResultCell)
configureNib(nibName: TableViewCellIdentifier.nothingFoundCell)
configureNib(nibName: TableViewCellIdentifier.loadingCell)
}
private func configureNib(nibName nibName: String){
let cellNib = UINib(nibName: nibName, bundle: nil)
tableView.registerNib(cellNib, forCellReuseIdentifier: nibName)
}
// MARK: UISearchBarDelegate
func searchBarSearchButtonClicked(searchBar: UISearchBar){
performSearch()
}
private func performSearch() {
if let category = Search.Category(rawValue: segmentedControl.selectedSegmentIndex){
search.performSearchForText(searchBar.text!, category: category){
success in
if !success{
self.showNetworkError()
}
if let controller = self.landscapeViewController{
controller.searchResultsRecived()
}
self.tableView.reloadData()
}
tableView.reloadData()
searchBar.resignFirstResponder()
}
}
func positionForBar(bar: UIBarPositioning) -> UIBarPosition {
return .TopAttached
}
// MARK: TableView
// MARK: - UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch search.state{
case .NotSearchedYet:
return 0
case .Loading,.NoResults:
return 1
case .Results(let list):
return list.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch search.state{
case .NotSearchedYet:
fatalError("Should never get here")
case .Loading:
let cell = tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.loadingCell, forIndexPath: indexPath)
let spinner = cell.viewWithTag(100) as! UIActivityIndicatorView
spinner.startAnimating()
return cell
case .NoResults:
return tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.nothingFoundCell, forIndexPath: indexPath)
case .Results(let list):
let cell = tableView.dequeueReusableCellWithIdentifier(TableViewCellIdentifier.searchResultCell, forIndexPath: indexPath) as! SearchResultCell
let searchResult = list[indexPath.row]
cell.configureForSearchResult(searchResult)
return cell
}
}
// MARK: UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("ShowDetail", sender: indexPath)
}
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
switch search.state{
case .NotSearchedYet,.NoResults,.Loading:
return nil
case .Results:
return indexPath
}
}
}
This is a bug in the Swift compiler, you'll want to file a radar with Apple to report this. There's nothing you can do about it otherwise.
I had this error because I had a switch of an enum with a case that contain an Array payload (don't ask me why, I don't know).
I think the enum is the search state inside the tableView:cellForRowAtIndexPath: method of SearchViewController and the prepareForSegue: method of LandscapeViewController (the .Result case contain a list).
In my case, changing the payload to a Set instead of an Array worked. (as a workaroud until it's fixed)

"type" viewController does not conform to protocol "UITableViewDataSource"

I keep getting this error for some reason.
"type" viewController does not conform to protocol "UITableViewDataSource"
I know that this has been covered a million times and there are even videos explaining why this problem occurs, but for some reason I can't fix it and can't figure out exactly what it is that I have done wrong.
Here is my code:
//
// ViewController.swift
// ChatApp
//
// Created by K on 17/03/2015.
// Copyright (c) 2015 Krazy88 All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
#IBOutlet weak var dockViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var messageTextField: UITextField!
#IBOutlet weak var messageTableView: UITableView!
var messagesArray:[String] = [String]()
var delegate: UITableViewDataSource? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.messageTableView.delegate = self
self.messageTableView.dataSource = self
// Set self as the delegate for the textfield
self.messageTextField.delegate = self
//Add a tap gesture recognizer to the tableview
let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target:self, action: "tableViewTapped")
self.messageTableView.addGestureRecognizer(tapGesture)
// Retrieve messages from Parse
self.retreiveMessages()
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendButtonTapped(sender: UIButton) {
// Send button is tapped
// Call the end editing method for the text field
self.messageTextField.endEditing(true)
//Disable the send button and textfield
self.messageTextField.enabled = false
self.sendButton.enabled = false
// Create a PFObject
var newMessageObject:PFObject = PFObject(className: "Message")
// Set the Text key to the text of the messageTextField
newMessageObject["Text"] = self.messageTextField.text
// Save the PFObject
newMessageObject.saveInBackgroundWithBlock { (success:Bool, error:NSError!) -> Void in
if (success == true) {
//Message has been saved!
// TODO: Retrieve the latest messages and reload the table
NSLog("Message saved successfully.")
}
else {
// Something bad happened.
NSLog(error.description)
}
// Enable the textfield and send button
self.sendButton.enabled = true
self.messageTextField.enabled = true
self.messageTextField.text = ""
}
}
func retreiveMessages() {
// Create a new PFQuery
var query:PFQuery = PFQuery(className: "Message")
// Call findobjectsinbackground
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
// clear the messageArray
self.messagesArray = [String]()
// Loop through the objects array
for messageObject in objects {
// Retrieve the Text column of each PFObject
let messageText:String? = (messageObject as PFObject)["Text"] as? String
// Assign it into our messagesArray
if messageText != nil {
self.messagesArray.append(messageText!)
}
}
// Reload the tableview
self.messageTableView.reloadData()
// Reload the tableview
}
func tableViewTapped() {
// Force the textfield to end editing
self.messageTextField.endEditing(true)
}
// MARK: Textfield Delegate Methods
func textFieldDidBeginEditing(textField: UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 350
self.view.layoutIfNeeded()
} , completion: nil)
}
func textFieldDidEndEditing(textField:UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 60
self.view.layoutIfNeeded()
}, completion: nil)
}
// MARK: TableView Delegate Methods
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create a table cell
let cell = self.messageTableView.dequeueReusableCellWithIdentifier("MessageCell") as UITableViewCell
// Customize the cell
cell.textLabel.text = self.messagesArray[indexPath.row]
//Return the cell
return cell
}
func TableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messagesArray.count
}
}
}
Any helps tips or suggestion will be much appreciated as I have tried everything!
thanks for reading
Kurando
Your UITableViewDataSource #required delegates are inside retreiveMessages(). Fix the position of that second last bracket..!!
Here is your correct code :
import Foundation
import UIKit
class ViewController : UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var dockViewHeightConstraint: NSLayoutConstraint!
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var messageTextField: UITextField!
#IBOutlet weak var messageTableView: UITableView!
var messagesArray:[String] = [String]()
var delegate: UITableViewDataSource? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
messageTableView.delegate = self
messageTableView.dataSource = self
// Set self as the delegate for the textfield
self.messageTextField.delegate = self
//Add a tap gesture recognizer to the tableview
let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target:self, action: "tableViewTapped")
self.messageTableView.addGestureRecognizer(tapGesture)
// Retrieve messages from Parse
self.retreiveMessages()
}
override func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func sendButtonTapped(sender: UIButton) {
// Send button is tapped
// Call the end editing method for the text field
self.messageTextField.endEditing(true)
//Disable the send button and textfield
self.messageTextField.enabled = false
self.sendButton.enabled = false
// Create a PFObject
var newMessageObject:PFObject = PFObject(className: "Message")
// Set the Text key to the text of the messageTextField
newMessageObject["Text"] = self.messageTextField.text
// Save the PFObject
newMessageObject.saveInBackgroundWithBlock { (success:Bool, error:NSError!) -> Void in
if (success == true) {
//Message has been saved!
// TODO: Retrieve the latest messages and reload the table
NSLog("Message saved successfully.")
}
else {
// Something bad happened.
NSLog(error.description)
}
// Enable the textfield and send button
self.sendButton.enabled = true
self.messageTextField.enabled = true
self.messageTextField.text = ""
}
}
func retreiveMessages() {
// Create a new PFQuery
var query:PFQuery = PFQuery(className: "Message")
// Call findobjectsinbackground
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error:NSError!) -> Void in
// clear the messageArray
self.messagesArray = [String]()
// Loop through the objects array
for messageObject in objects {
// Retrieve the Text column of each PFObject
let messageText:String? = (messageObject as PFObject)["Text"] as? String
// Assign it into our messagesArray
if messageText != nil {
self.messagesArray.append(messageText!)
}
}
// Reload the tableview
self.messageTableView.reloadData()
// Reload the tableview
}
}
func tableViewTapped() {
// Force the textfield to end editing
self.messageTextField.endEditing(true)
}
// MARK: Textfield Delegate Methods
func textFieldDidBeginEditing(textField: UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 350
self.view.layoutIfNeeded()
} , completion: nil)
}
func textFieldDidEndEditing(textField:UITextField) {
// Perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 60
self.view.layoutIfNeeded()
}, completion: nil)
}
// MARK: TableView Delegate Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return messagesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create a table cell
let cell = self.messageTableView.dequeueReusableCellWithIdentifier("MessageCell") as UITableViewCell
// Customize the cell
cell.textLabel?.text = self.messagesArray[indexPath.row]
//Return the cell
return cell
}
}
Copy from here.
And always make sure to call numberOfRowsInSection before cellForRowAtIndexPath.

swift - adding values between 2 view controllers by prepareforsegue

I have 2 ViewControllers: ViewController1, ViewController2. An "Add" button in ViewController1 will bring up ViewController2. User will then enter in values into ViewController2 which will then be passed back into ViewController1 by prepareforsegue. These values are then append onto the properties of type array in viewcontroller1.
This is being repeated, whereby the user continue pressing the add button to bring up ViewController2 from ViewController1, and then add in new values to be appended onto the properties of array type in ViewController1 via prepareforsegue.
However this is not the case after the first set of values being appended from ViewController2. The second set of values will overwrite the first set of values.
Example.
After the first passed -> force =[1], stiffness[1]
After the second passed -> force=[2], stiffness[2]
I will want this -> force = [1,2], stiffness[1,2]
I want to continue adding until -> force = [1,2,3,4,5], stiffness[1,2,3,4,5]
ViewController1
class ViewController1: UITableViewController, UITableViewDataSource {
var force = [Float]()
var stiffness = [Float] ()
#IBAction func Add(sender: AnyObject) { }
}
ViewController2
class ViewController2: UIViewController {
var forceVar : Float = 0.0
var stiffVar : Float = 0.0
#IBAction func submit(sender: AnyObject) {
self.performSegueWithIdentifier("springSubmit", sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if(segue.identifier == "springSubmit") {
var svcSubmitVariables = segue.destinationViewController as ViewController1
svcSubmitVariables.force.append(forceVar)
svcSubmitVariables.stiffness.append(stiffVar)
}
The performSegueWithIdentifier method will always initialize new view controller so in your example view controller that showed ViewController2 is different object from object initialized after submitting data and ofc this newly initialized object will have only initialize values. To achieve what you are looking for you will need to declare a function on your ViewController1 that will append data to your arrays, pass that function to ViewController2 and call it on submit method so your view controllers would look similar like these:
ViewController1
class ViewController1: UITableViewController, UITableViewDataSource {
var force = [Float]()
var stiffness = [Float] ()
override func viewWillAppear(animated: Bool) {
//check if values are updated
println(force)
println(stiffness)
}
#IBAction func Add(sender: AnyObject) {
self.performSegueWithIdentifier("viewController2", sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if(segue.identifier == "viewController2") {
var addVariables = segue.destinationViewController as ViewController2
addVariables.submitFunc = appendData
}
}
func appendData(newForce:Float,newStiffness:Force){
force.append(newForce)
stiffness.append(newStiffness)
}
}
ViewController2
class ViewController2: UIViewController {
var forceVar : Float = 0.0
var stiffVar : Float = 0.0
var submitFunc:((newForce:Float,newStiff:Float)->())!
#IBAction func submit(sender: AnyObject) {
submitFunc(forceVar,stiffVar)
self.dismissViewControllerAnimated(false, completion: nil)
}
}
ViewController
import UIKit
class ViewController: UITableViewController, UITableViewDataSource {
var springNumber:NSInteger = 0
var force = [Float]()
var stiffness = [Float] ()
// MARK: UITableViewDataSource
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return springNumber
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")
as UITableViewCell
cell.textLabel!.text = "Spring \(indexPath.row)"
return cell
}
func fwall() -> Float {
for rows in 0..<(springNumber+1) {
force[0] = force[0] + force[rows]
}
force[0] = -(force[0])
return force[0]
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
title = "Springs' Variables"
tableView.registerClass(UITableViewCell.self,
forCellReuseIdentifier: "Cell")
if(springNumber==0) {
force.append(0.0) }
println(force)
println(stiffness)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//Adding Values
#IBAction func Add(sender: AnyObject) {
self.performSegueWithIdentifier("addSpring", sender: sender)
}
#IBAction func solve_Pressed(sender: AnyObject) {
self.fwall()
self.performSegueWithIdentifier("Solve", sender: sender)
}
func appendData (newForce: Float, newStiffness:Float, newSpring: NSInteger) {
force.append(newForce)
stiffness.append(newStiffness)
springNumber = newSpring
println(springNumber)
println(force)
println(stiffness)
}
override func prepareForSegue ( segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "addSpring") {
var addVariables = segue.destinationViewController as SpringTableViewControllerInsertVariables
addVariables.submitFunc = appendData
}
if (segue.identifier == "Solve") {
var svcViewController2 = segue.destinationViewController as ViewController2
svcViewController2.forceView2 = self.force
svcViewController2.stiffView2 = self.stiffness
svcViewController2.springNumView2 = self.springNumber
}
if (segue.identifier == "showDetail") {
}
}
}
Code for springTableViewControllerInsertVariables
import UIKit
class SpringTableViewControllerInsertVariables: UIViewController {
var forceVar : Float = 0.0
var stiffVar : Float = 0.0
var springNum : NSInteger = 0
var submitFunc: ((newForce:Float,newStiff:Float,newSpring:NSInteger)->())!
#IBOutlet weak var image: UIImageView!
var imageArray :[UIImage] = [UIImage(named: "springAtWall.jpg")!, UIImage(named:"spring.jpg")!]
override func viewDidLoad() {
if(springNum == 0) {image.image = imageArray[0] }
else {image.image = imageArray[1] }
}
#IBOutlet weak var force: UILabel!
#IBOutlet weak var forceEntered: UITextField!
#IBOutlet weak var stiffness: UILabel!
#IBOutlet weak var stiffnessEntered: UITextField!
#IBAction func checkboxed(sender: AnyObject) {
//when checkedboxed is checked here.
//1)UIImage is changed
//2)calculate2 function is used for calculate1
}
#IBAction func submit(sender: AnyObject) {
//might not work because springNum will be initialise to zero when this viewcontroller starts...
forceVar = (forceEntered.text as NSString).floatValue
stiffVar = (stiffnessEntered.text as NSString).floatValue
springNum = springNum + 1
submitFunc(newForce: forceVar ,newStiff: stiffVar, newSpring: springNum)
self.dismissViewControllerAnimated(false, completion: nil)
}
}

Resources