I have table view like this (in a mac cocoa application):
In the leftmost panel you can see that I have set the identifier of the Table Cell View to "1". That's fine if you just have 2 columns, once the number goes up, this approach will become cumbersome. Can I do this programmatically?
Here is an example:
import Cocoa
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
private var dataModel = DataModel()
private var answer = 0
private var keyData: (Int, [Int]) = (0, []) {
didSet {
tbl.reloadData()
}
}
#IBOutlet weak var questionIndex: NSTextField!
#IBOutlet weak var tbl: NSTableView!
#IBAction func replay(_ sender: Any) {
dataModel = DataModel()
questionIndex.stringValue = "0:"
answer = 0
updateModel()
}
#IBAction func forward(_ sender: NSButton) {
if sender.tag == 1 {
answer += keyData.0
}
updateModel()
}
func updateModel() {
let group = dataModel.nextGroup()
if let g = group {
self.keyData = g
let s = questionIndex.stringValue
questionIndex.stringValue = String(Int(String(s.characters.dropLast()))! + 1) + ":"
return
}
let alert = NSAlert()
alert.messageText = "You did have \(answer) on your mind, didn't you?"
alert.runModal()
}
override func viewDidLoad() {
super.viewDidLoad()
for (n, col) in tbl.tableColumns.enumerated() {
col.identifier = String(n)
}
updateModel()
}
func numberOfRows(in tableView: NSTableView) -> Int {
return keyData.1.count / 8 + 1
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let colId = tableColumn!.identifier
let colIndex = Int(colId)!
let index = (row * 8) + colIndex
let cell = tbl.make(withIdentifier: colId, owner: self) as! NSTableCellView
if 0 <= index && index < keyData.1.count {
cell.textField!.integerValue = keyData.1[index]
} else {
cell.textField!.stringValue = ""
}
return cell
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I have assigned the cell identifiers by hand, and made them identical the corresponding column index, so as to creating a mapping between the cell id and the 2D array (which is the underlying data model) column index. The app is running fine, I just don't like assigning these IDs by click-and-point.
The full project can be found here: https://github.com/kindlychung/MysteriousNum
Create custom cell and add init to it using following lines.
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
and register this cell class as.
self.tableView.register(CustomCell.self, forCellReuseIdentifier: "customCell")
also dequeueReusableCell using same cell like:
tableView.dequeueReusableCell(withIdentifier: "customCell",for: indexPath) as! CustomCell
Related
This is the data model I used to import a json file.
struct ActionResult: Codable {
let data: [Datum]
}
struct Datum: Codable {
let goalTitle, goalDescription, goalImage: String
let action: [Action]
}
struct Action: Codable {
let actionTitle: String
let actionGoal: String
Now I am trying to create a searchbar, but it only searches in one section, although I have not defined a section in the data model but it is picking either the goalTitle or actionGoal.
var index = 0
var action: Action? // this is for the segue
var result: ActionResult? {
didSet {
guard let result = result else { return }
allSectionDataActionMap = Dictionary(uniqueKeysWithValues: result.data.enumerated().map { ($0.0, ($0.1, $0.1.action)) })
updateFilteredData()
}
}
var allSectionDataActionMap = [Int: (datum: Datum, actions: [Action])]()
// Maps the section index to the Datum & filtered [Action]
var filteredSectionDataActions = [Int: (datum: Datum, actions: [Action])]()
let searchController = UISearchController()
This is part of the setup code.
override func numberOfSections(in tableView: UITableView) -> Int {
return filteredSectionDataActions.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredSectionDataActions[section]?.actions.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ActionTableView.dequeueReusableCell(withIdentifier: "ActionCell", for: indexPath) as! ActionTableCell
let action = filteredSectionDataActions[indexPath.section]?.actions[indexPath.row]
// setup cell for action
cell.actionItem.text = action?.actionTitle
cell.actionImage.image = UIImage(named: action!.actionImage)
cell.actionImage.layer.cornerRadius = cell.actionImage.frame.size.width / 2
cell.actionGoal.text = action?.actionGoal
// cell.actionBenefit.text = action?.actionBenefit
// cell.actionCalculator.text = action?.actionCalculator
This is the table view code
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
updateFilteredData(for: searchText.lowercased())
tableView.reloadData()
}
func updateFilteredData(for searchText: String = String()) {
if searchText.isEmpty {
filteredSectionDataActions = allSectionDataActionMap
} else {
for (index, (datum, actions)) in allSectionDataActionMap {
let filteredActions = actions.filter { $0.actionTitle.lowercased().contains(searchText) }
if filteredActions.isEmpty {
filteredSectionDataActions[index] = (datum, actions)
} else {
filteredSectionDataActions[index] = (datum, filteredActions)
}
This is for the searchbar.
I am using cell-based tableview to store custom class data, for example
class Person {
var name:String = ""
var age: Int = 0
}
And then use a NSMUtableArray to collect person1, person2 ... datas, call it dataArray.
Present Person name on column1 and age on column2, works fine and editable.
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
if tableColumn?.identifier == "Name"
{
return (self.dataArray[row] as! Person).name
}
else if tableColumn?.identifier == "Age"
{
return (self.dataArray[row] as! Person).age
}
}
How can I sort each column with respected sortDescriptor?
I follow that fefernce but "sortDescriptorWithKey:tableColumn.identifier" always fail in my code, it can be passed if set the tableColumn.identifier to "self", it also confused me.
Below are my other code fragments.
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
let newDescriptors = tableView.sortDescriptors
dataArray.sort(using: newDescriptors)
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Sort
for tableColumn in tableView.tableColumns {
let sortDescrptor = NSSortDescriptor.init(key: tableColumn.identifier, ascending: true, selector: #selector(NSString.caseInsensitiveCompare))
tableColumn.sortDescriptorPrototype = sortDescrptor
}
}
I'd like to read data from an array and write data to a file and viceversa. The file itself has a known path.
The BTTableCell class manages and populates the tableView.
I'm using dati_Riga class in order to have a single array. I thought it would be easier to read and save data using WriteToFile in order to write data and NSMutableArray to read them, but I'm having trouble.
In the following code snippet of the ViewController file you can see functions I wrote in order to open and save data which don't work.
Where did I get wrong? My idea is to save data using BTTableCell but I wasn't able to make it work.
Data structure:
import Cocoa
class BPTableCell:NSTableCellView {
#IBOutlet weak var item_IconaFile: NSImageView!
#IBOutlet weak var item_NomeFile: NSTextField!
#IBOutlet weak var item_PathFile: NSTextField!
#IBOutlet weak var item_MD5: NSTextField!
#IBOutlet weak var item_SHA1: NSTextField!
}
class dati_Riga {
var dati_Riga_item_IconaFile: NSImage!
var dati_Riga_item_NomeFile: String!
var dati_Riga_item_PathFile: String!
var dati_Riga_item_MD5: String!
var dati_Riga_item_SHA1: String!
init (dati_Riga_item_IconaFile: NSImage, dati_Riga_item_NomeFile: String, dati_Riga_item_PathFile: String, dati_Riga_item_MD5: String, dati_Riga_item_SHA1: String)
{
self.dati_Riga_item_IconaFile = dati_Riga_item_IconaFile
self.dati_Riga_item_NomeFile = dati_Riga_item_NomeFile
self.dati_Riga_item_PathFile = dati_Riga_item_PathFile
self.dati_Riga_item_MD5 = dati_Riga_item_MD5
self.dati_Riga_item_SHA1 = dati_Riga_item_SHA1
}
}
Extract from "ViewController.swift":
import Cocoa
import Foundation
import AppKit
import CryptoSwift
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
#IBOutlet weak var tableview: NSTableView!
var item_IconaFile = [NSImage]()
var item_NomeFile = [String]()
var item_PathFile = [String]()
var item_MD5 = [String]()
var item_SHA1 = [String]()
var miei_Dati_Riga : [dati_Riga] = [dati_Riga(dati_Riga_item_IconaFile: NSImage(), dati_Riga_item_NomeFile: "", dati_Riga_item_PathFile: "", dati_Riga_item_MD5: "", dati_Riga_item_SHA1: "")]
override func viewDidLoad() {
super.viewDidLoad()
self.miei_Dati_Riga.removeAtIndex(0)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
// MARK: Funzioni relative la TableView
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let result : BPTableCell = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! BPTableCell
result.item_IconaFile.image = item_IconaFile[row]
result.item_NomeFile.stringValue = item_NomeFile[row]
result.item_PathFile.stringValue = item_PathFile[row]
result.item_MD5.stringValue = item_MD5[row]
result.item_SHA1.stringValue = item_SHA1[row]
return result
}
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
let num_Righe = item_NomeFile.count
return num_Righe
}
// MARK: Apri e Salva file
#IBAction func Save_File(sender: NSMenuItem) {
let salva_File = NSSavePanel()
salva_File.extensionHidden = true
salva_File.canSelectHiddenExtension = true
salva_File.allowedFileTypes = ["cpa"]
salva_File.beginWithCompletionHandler { (result:Int) -> Void in
if result == NSFileHandlingPanelOKButton {
let save_URL_File = salva_File.URL
let mio_Array: NSArray = self.miei_Dati_Riga
print(self.miei_Dati_Riga.count)
print(self.miei_Dati_Riga[0])
if mio_Array.writeToFile((save_URL_File?.path)!, atomically: true) {
print("File Salvato")}
else {
print("File Non Salvato")
}
}
}
}
#IBAction func Open_File(sender: NSMenuItem) {
let apri_File = NSOpenPanel()
apri_File.allowedFileTypes = ["cpa"]
apri_File.beginWithCompletionHandler { (result:Int) -> Void in
if result == NSFileHandlingPanelOKButton {
let contenuto_File = NSMutableArray (contentsOfURL: apri_File.URL!)
self.tableview.reloadData()
}
}
}
I am struggling to pass a variable received from a drag and drop (file from finder onto NSView) to a NSArrayController in AppDelegate.
NSView receives the drag and drop:
class MyView:NSView, NSDraggingDestination {
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
}
let fileTypes = ["jpg", "jpeg", "bmp", "tif", "TIF"]
var fileTypeIsOk = false
var droppedFilePath = ""
required init?(coder: NSCoder) {
let types = [NSFilenamesPboardType, NSURLPboardType, NSPasteboardTypeTIFF]
super.init(coder: coder)
registerForDraggedTypes(types)
}
override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
if checkExtension(sender) == true {
fileTypeIsOk = true
return .Every
} else {
fileTypeIsOk = false
println("Dropped images rejected.")
return .None
}
}
override func performDragOperation(sender: NSDraggingInfo) -> Bool {
var go = false
if let board = sender.draggingPasteboard().propertyListForType("NSFilenamesPboardType") as? NSArray {
for indexFile in 0...board.count-1 {
println("Files dropped \(board[indexFile])")
//-> pass the variable board[indexFile] or board[] to NSArrayController
go = true
}
}
return go
}
func checkExtension(drag: NSDraggingInfo) -> Bool {
var go = false
var foundExtension = 0
var numberOfFiles = 0
if let board = drag.draggingPasteboard().propertyListForType("NSFilenamesPboardType") as? NSArray {
numberOfFiles = board.count
for indexFile in 0...numberOfFiles-1 {
if let url = NSURL(fileURLWithPath: (board[indexFile] as! String)) {
let suffix = url.pathExtension!
for ext in fileTypes {
if ext.lowercaseString == suffix {
++foundExtension
break
}
}
}
}
}
if foundExtension == numberOfFiles {
println("Dropped files have valid extension.")
go = true
} else {
println("At least one file dropped has an invalid extension.")
}
return go
}
}
in AppDelegate, I have set up an NSArrayController (pointing to data[] at the moment but I don't know how to populate data[] from board[])
func numberOfRowsInTableView(aTableView: NSTableView) -> Int {
return data.count
}
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
println("Reload Array")
if tableColumn!.identifier == "fileNameColumn" {
cellView.imageView!.image = NSImage(named: "GreenCircle")
cellView.textField!.stringValue = data[row]
return cellView
}
...
thank you for your help
Call the "- (void)addObjects:(NSArray *)objects;" method on your NSArrayController and pass it board:
[self.myArrayController addObjects:board];
It worked by adding in NSView: performDragOperation:
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
then I can reach the 'data' variable using appDelegate.data.
I have implemented a basic example of an ios app using Realm.io
I'd like to be able to reorder table rows in my iOS app and save the order back to Realm.
Realm model contains a property called position for this purpose.
P.S: Sorry for so much code.
import UIKit
import Realm
class Cell: UITableViewCell {
var position: Int!
init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: .Subtitle, reuseIdentifier: reuseIdentifier)
}
}
class Language: RLMObject {
var title = ""
var position = Int()
}
class ManagerLanguagesController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
var array = RLMArray()
var notificationToken: RLMNotificationToken?
var editButton = UIBarButtonItem()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
notificationToken = RLMRealm.defaultRealm().addNotificationBlock { note, realm in
self.reloadData()
}
reloadData()
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return Int(array.count)
}
func setupUI() {
tableView.registerClass(Cell.self, forCellReuseIdentifier: "cell")
self.title = "Languages"
var addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "add")
editButton = UIBarButtonItem(title: "Edit", style: .Plain, target: self, action: "edit")
var buttons = [addButton, editButton]
self.navigationItem.rightBarButtonItems = buttons
}
func add() {
var addLanguageView:UIViewController = self.storyboard.instantiateViewControllerWithIdentifier("newLanguage") as UIViewController
self.navigationController.presentViewController(addLanguageView, animated: true, completion: nil)
}
func edit () {
if tableView.editing {
/* FROM THIS POINT I'M PROBABLY DOING SOMETHING WRONG.. IT IS NOT WORKING */
var positionArray = NSMutableArray()
let realm = RLMRealm.defaultRealm()
var i = 0
for var row = 0; row < tableView.numberOfRowsInSection(0); row++ {
var cellPath = NSIndexPath(forRow: row, inSection: 0)
var cell:Cell = tableView.cellForRowAtIndexPath(cellPath) as Cell
positionArray.addObject(cell.position)
}
realm.beginWriteTransaction()
for row: RLMObject in array {
row["position"] = positionArray[i]
i++
}
realm.commitWriteTransaction()
/* -- NOT WORKING END -- */
tableView.setEditing(false, animated: true)
editButton.style = UIBarButtonItemStyle.Plain
editButton.title = "Edit"
} else{
tableView.setEditing(true, animated: true)
editButton.title = "Done"
editButton.style = UIBarButtonItemStyle.Done
}
}
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as Cell
let object = array[UInt(indexPath!.row)] as Language
cell.textLabel.text = object.title
cell.position = object.position // I have implemented this to be able to retain initial positions for each row and maybe use this when reordering..
return cell
}
override func tableView(tableView: UITableView!, canMoveRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
return true
}
override func tableView(tableView: UITableView!, moveRowAtIndexPath sourceIndexPath: NSIndexPath!, toIndexPath destinationIndexPath: NSIndexPath!) {
// println("Old index: \(sourceIndexPath.indexAtPosition(sourceIndexPath.length - 1)+1)")
// println("New index: \(destinationIndexPath.indexAtPosition(sourceIndexPath.length - 1)+1)")
// Maybe something needs to be implemented here instead...
}
func reloadData() {
array = Language.allObjects().arraySortedByProperty("position", ascending: true)
tableView.reloadData()
}
}
Thanks in advance
Instead of using a position property, you could instead keep an ordered array as a property on another object. This way you don't have to keep the position up to date and instead arrange your objects as needed:
class Language: RLMObject {
dynamic var title = ""
}
class LanguageList: RLMObject {
dynamic var languages = RLMArray(objectClassName: "Language")
}
class ManagerLanguagesController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
// create our list
var realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
realm.addObject(LanguageList())
realm.commitWriteTransaction()
...
}
// helper to get the RLMArray of languages in our list
func array() -> RLMArray {
return (LanguageList.allObjects().firstObject() as LanguageList).languages
}
override func tableView(tableView: UITableView!, moveRowAtIndexPath sourceIndexPath: NSIndexPath!, toIndexPath destinationIndexPath: NSIndexPath!) {
var languages = array()
var object = languages.objectAtIndex(UInt(sourceIndexPath.row)) as Language
var realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
languages.removeObjectAtIndex(UInt(sourceIndexPath.row))
languages.insertObject(object, atIndex: UInt(destinationIndexPath.row))
realm.commitWriteTransaction()
}
...
}
this work for me to move rows in tableview using realm with swift 2.2:
func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
let aux = TimesHome.mutableCopy() as! NSMutableArray
let itemToMove = aux[fromIndexPath.row]
let realm = try! Realm()
realm.beginWrite()
aux.removeObjectAtIndex(fromIndexPath.row)
aux.insertObject(itemToMove, atIndex: toIndexPath.row)
try! realm.commitWrite()
TimesHome = aux
let times = realm.objects(ParciaisTimes)
if times.count > 0 {
for tm in times {
for i in 1...aux.count {
if aux[i-1].valueForKey("time_id") as! Int == tm.time_id {
realm.beginWrite()
tm.ordem = i
try! realm.commitWrite()
}
}
}
}
}