NSTableview - Drag and Drop split into two classes and controllers - macos

i found this helpfully tutorial for realize drag an drop with nstabelview:
https://drive.google.com/open?id=0B8PBtMQt9GdONzV3emZGQWUtdmM
this works fine.
but i would like to split both table views into differente view controllers and classes with a split view:
one split view controller:
item 1: viewcontroller with source nstableview (SourceTableView.class)
item 2: viewcontroller with target nstableview (TargetTableView.class)
how can i do this with this project?
i know how can i create a split view controller in storyboard.
but i dont know, if i have two different classes, how the iBoutlet SourceTabelView of class SourceTableView.class assign the iBoutlet TargetTableView of class TargetTableView.class
UPDATE
var person = [Person]()
NSManagedObject.class
import Foundation
import CoreData
#objc(Person)
public class Person: NSManagedObject {
#NSManaged public var firstName: String
#NSManaged public var secondName: String
}

Example of drag and drop between two table views inside a split view. Dragging inside one table view and multiple selection will work. Hold the Option key to drag a copy.
The datasource of each table view is the view controller inside the split view. Each table view has its own view controller and each view controller controls one table view. Both view controllers are the same NSViewController subclass:
class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet weak var myTableView: NSTableView!
var dataArray: NSMutableArray = ["John Doe", "Jane Doe", "Mary Jane"]
override func viewDidLoad() {
super.viewDidLoad()
myTableView.register(forDraggedTypes: ["com.yoursite.yourproject.yourstringstype"])
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
// NSTableViewDataSource data methods
func numberOfRows(in tableView: NSTableView) -> Int {
return dataArray.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
return dataArray[row] as AnyObject!;
}
// NSTableViewDataSource drag methods
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
// the dragging destination needs the strings of the rows to add to its own data,
// we, the dragging source, need the indexes of the rows to remove the dropped rows.
pboard.declareTypes(["com.yoursite.yourproject.yourstringstype", "com.yoursite.yourproject.yourindexestype"],
owner: nil)
pboard.setData(NSKeyedArchiver.archivedData(withRootObject: (dataArray as NSArray).objects(at:rowIndexes as IndexSet)), forType: "com.yoursite.yourproject.yourstringstype")
pboard.setData(NSKeyedArchiver.archivedData(withRootObject: rowIndexes), forType: "com.yoursite.yourproject.yourindexestype")
return true
}
func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession,
endedAt screenPoint: NSPoint, operation: NSDragOperation) {
// remove the dragged rows if the rows are dragged to the trash or were moved to somewhere else.
var removeRows = false
if operation == .delete {
// trash
removeRows = true
} else if operation == .move {
// check if the point where the rows were dropped is inside our table view.
let windowRect = tableView.convert(tableView.bounds, to: nil)
let screenRect = view.window!.convertToScreen(windowRect)
if !NSPointInRect(screenPoint, screenRect) {
removeRows = true
}
}
if removeRows {
// remove the rows, the indexes are on the pasteboard
let data = session.draggingPasteboard.data(forType: "com.yoursite.yourproject.yourindexestype")!
let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: data) as! NSIndexSet
(dataArray as NSMutableArray).removeObjects(at: rowIndexes as IndexSet)
tableView.reloadData()
}
}
// NSTableViewDataSource drop methods
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int,
proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
// only accept drop above rows, not on rows.
if dropOperation == .above {
// return move if the dragging source allows move
if info.draggingSourceOperationMask().contains(.move) {
return .move
}
// return copy if the dragging source allows copy
if info.draggingSourceOperationMask().contains(.copy) {
return .copy
}
}
return []
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int,
dropOperation: NSTableViewDropOperation) -> Bool {
// if the rows were moved inside the same table view we do a reorder
var dropRow = row
if info.draggingSource() as AnyObject === myTableView as AnyObject &&
info.draggingSourceOperationMask().contains(.move) {
// remove the rows from their old position
let data = info.draggingPasteboard().data(forType: "com.yoursite.yourproject.yourindexestype")!
let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: data) as! NSIndexSet
(dataArray as NSMutableArray).removeObjects(at: rowIndexes as IndexSet)
// recalculate the row of the drop
dropRow -= rowIndexes.countOfIndexes(in: NSMakeRange(0, dropRow))
}
// insert the dragged rows
let data = info.draggingPasteboard().data(forType: "com.yoursite.yourproject.yourstringstype")!
let draggedStrings = NSKeyedUnarchiver.unarchiveObject(with: data) as! [Any]
dataArray.insert(draggedStrings, at:IndexSet(integersIn:dropRow..<(dropRow + draggedStrings.count)))
tableView.reloadData()
return true
}
}
To make dragging to the trash work, subclass NSTableView and override:
override func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor
context: NSDraggingContext) -> NSDragOperation {
let test = super.draggingSession(session, sourceOperationMaskFor: context)
Swift.print("sourceOperationMaskFor \(test)")
switch context {
case .withinApplication:
return [.move, .copy]
case .outsideApplication:
return [.delete]
}
}
p.s. I'm not familiar with Swift and had some trouble with arrays and indexsets so I used NSMutableArray and NSIndexSet.

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.

Xcode 7 - Cell content not displayed into an empty label

I'm trying to display a cell content into an empty UILabel, but after I upgraded to Xcode 7, the content didn't show up. I was following the same mechanism and it worked on Xcode 6, but since I'm new to Swift I may have something wrong in the code.
TableViewController:
import UIKit
class TableViewController: UITableViewController {
struct dataModel {
var name:String
var val:Double
}
let foods = [dataModel(name: "name1", val: 3.3),
dataModel(name: "name2", val: 5.5)]
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// 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 Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return foods.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
// Configure the cell...
let foodCell = foods[indexPath.row]
cell.textLabel?.text = foodCell.name
return cell
}
var valueToPass:String!
var valueInt:Int!
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel!.text
performSegueWithIdentifier("showCalc", sender: self)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showCalc") {
let viewController = segue.destinationViewController as! calcViewController
viewController.passedValue = valueToPass
}
}
}
ViewController where I want to display the tapped cell into an empty label:
import UIKit
class calcViewController: UIViewController {
#IBOutlet var text: UILabel!
#IBOutlet var empty1: UILabel!
var passedValue:String!
override func viewWillAppear(animated: Bool) {
empty1.text = passedValue
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
As a result, there is no error, but there is nothing displayed inside the empty label.
The other thing I want to achieve is to display the Int value into another label and then do some calculations, but this would be another question. Thanks in advance!
There are several issues in this code. First you are obviously missing some closing braces. But I assume this is a copy-paste error.
Why do you use didDeselectRowAtIndexPath? Don't you have a segue from the table view cell to the next controller? Then you should get the text label in prepareForSegue by using tableView.indexPathForSelectedRow.
If you like to keep it this way you should use didSelect... in stead of didDeselect....
You should take the value you want to pass to the next view controller direct from the data and not from the cell. You have the data you present in the table in the foods array.
Class names should always start with a capital letter. ALWAYS!
Having the data model within the controller is poor design. You should have the struct separated in its own file. In this case you could even hand the complete data object to the next view controller.

Save UITableView names to array on button action

I want to save the names that are filled in UITableView to an array
var mineSpillere = [String]()
How can I do this on buttonAction?
This is button action for how i add names to tableview:
#IBAction func addButtonAction(sender: AnyObject) {
mineSpillere.append(namesTextBox.text)
myTableView.reloadData()
}
and also here code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.myTableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
cell.textLabel!.text = self.mineSpillere[indexPath.row]
return cell;
}
That only when I press the "back" button in the application, the names from the UITableView will be saved in an array.
I am going to access these names from another view controller also, so i need them to be saved as an array.
Okay so it looks like you have all the data in the mineSpillere array already as you're using cell.textLabel!.text = self.mineSpillere[indexPath.row] so to get that array back to the first view controller. You could use the following code in the view controller that is being presented when the 'back' button is being pressed:
class FirstViewController: UIViewController,SecondViewControllerDelegate {
var mineSpillereCopyFromSecondVC = [String]()
func presentSecondViewController() {
// Im not sure the class name for you're second view
// where the tableView is located.
var secondVC: SecondViewController = SecondViewController();
secondVC.delegate = self
// Also not too sure on how you're presenting the `SecondViewController` so I'm goingg to leave that empty.
// The important part is just above us.
}
func passMineSpillere(mineSpillere: [String]) {
// This class now has the mineSpiller array of name.
self.mineSpillereCopyFromSecondVC = mineSpillere
}
}
And then in you SecondViewController where the tableView is located you could add the following code.
import UIKit
protocol SecondViewControllerDelegate {
func passMineSpillere(mineSpillere:[String])
}
class SecondViewController: UIViewController {
var delegate: SecondViewControllerDelegate?
var mineSpillere = [String]()
// This will be called when the 'back' button is pressed.
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// Pass the mineSpillere array back to the delegate
// which is the firstViewController instance.
self.delegate?.passMineSpillere(self.mineSpillere)
}
}

Replacing row view of NStableView on selecting a row

I have a NsTableView and I want to use two type NSTableRowView for rows of table. For selected row There is complex NStableRowView and for non selected row there will be simple NSTableRowView with two labels.
Here is what I have tried --
extension CMMAccountSettingViewController:NSTableViewDelegate{
func tableView(tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
if row == accountListTableView.selectedRow{
var storyboard = NSStoryboard(name: "Settings", bundle: nil)
var accountController = storyboard?.instantiateControllerWithIdentifier("account") as! CMMAccountViewController
var rowView = accountController.view as! CMMAccountView
return rowView
}
else
{
var rowView = accountListTableView.makeViewWithIdentifier("accountSeetingsRow", owner: self) as! CMMAccountRowView
rowView.titleLable.stringValue = listOfAccounts[row].nickname!
rowView.captionLable.stringValue = listOfAccounts[row].accountDisplayName!
return rowView
}
}
func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
if accountListTableView.selectedRow == row{
return 350
}
else{
return 75
}
}
func tableViewSelectionDidChange(notification: NSNotification) {
var coloumnIndexSet = NSIndexSet(indexesInRange: NSMakeRange(0, accountListTableView.tableColumns.count))
accountListTableView.reloadDataForRowIndexes(accountListTableView.selectedRowIndexes, columnIndexes: coloumnIndexSet)
}
}
But This approach is not working. How can I replace selected row view with animation?
Thanks for your help.

Passing variables between classes in Swift

I have a table view controller named ThirdViewController which displays a list of transactions. When a user presses the add button, the app segues to the FifthViewController where they can enter the transaction name, value and date. I am trying to get this data entered in FifthViewController into a class named ArrayData so that I can add it to the array which will then add the payment to the ThirdViewController table view.
This is my code for the ThirdViewController.swift:
var arrayObject = ArrayData()
class ThirdViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 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.
}
// #pragma mark - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return arrayObject.paymentsArray().count
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:CustomTransactionTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell
cell.paymentNameLabel.text = (arrayObject.paymentsArray().objectAtIndex(indexPath.row)) as String
cell.costLabel.text = (arrayObject.costArray().objectAtIndex(indexPath.row)) as String
cell.dateLabel.text = (arrayObject.dateArray().objectAtIndex(indexPath.row)) as String
return cell
}
}
This is the code for arrayData.swift:
class ArrayData: NSObject {
func paymentsArray() -> NSMutableArray {
var arrayDataPayments: NSMutableArray = ["Test"]
return arrayDataPayments
}
func costArray() -> NSMutableArray {
var arrayDataCost: NSMutableArray = ["£100"]
return arrayDataCost
}
func dateArray() -> NSMutableArray {
var arrayDataDate: NSMutableArray = ["12/07/14"]
return arrayDataDate
}
}
And this is the code for FifthViewController:
class FifthViewController: UIViewController {
#IBOutlet var transactionNameInput : UITextField
#IBOutlet var transactionDateInput : UITextField
#IBOutlet var transactionValueInput : UITextField
#IBOutlet var addBackButton : UIButton
#IBAction func addTransactionButton(sender : AnyObject) {
self.navigationController.popToRootViewControllerAnimated(true)
var transactionName = transactionNameInput.text
var transactionDate = transactionDateInput.text
var transactionValue = transactionValueInput.text
}
#IBAction func viewTapped(sender : AnyObject) {
//Closes keyboard when user touches screen
transactionDateInput.resignFirstResponder()
transactionNameInput.resignFirstResponder()
transactionValueInput.resignFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
How can I get the variables from the FifthViewController to the ArrayData class so they can be added to the arrays? This might be very simple but I'm new to Xcode and obviously Swift.
Any help would be greatly appreciated.
The methods in your ArrayData class are awkwardly implemented as each invocation returns a new array. Perhaps this is what you really want, but it is unusual (and you've noted that you are new to Xcode and Swift and thus I suspect it isn't really what you want). A more normal implementation would be :
class ArrayData: NSObject {
var arrayDataPayments: NSMutableArray = ["Test"]
var arrayDataCost: NSMutableArray = ["£100"]
var arrayDataDate: NSMutableArray = ["12/07/14"]
func paymentsArray() -> NSMutableArray { return arrayDataPayments }
func costArray() -> NSMutableArray { return arrayDataCost }
func dateArray() -> NSMutableArray { return arrayDataDate }
}
Now, I'm not saying that this is going to solve your bigger issue of getting the data in an out of your controller; but, I suspect that the above is a better start.

Resources