Reordering Realm.io data in tableView with Swift - xcode

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()
}
}
}
}
}

Related

Searchbar only searches in first section - actionGoal or goalTitle

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.

Set identifier of table cell view programmatically

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

swift: refresh/reload table view did not work

i'm new in swift development, i added data in server tried to refresh tableviewcontroller with refreshcontrol function but value in table view didn't change.
class MainTableViewController: UITableViewController, UINavigationControllerDelegate {
#IBOutlet var sosTableView: UITableView!
var datas = [dataSos]()
override func viewDidLoad() {
super.viewDidLoad()
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.labelText = "Loading"
spinningActivity.detailsLabelText = "Please wait"
dispatch_async(dispatch_get_main_queue()) {
self.loadDataServer()
spinningActivity.hide(true)
self.sosTableView.reloadData()
}
//loadDataSos()
// 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()
var refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: Selector("refreshData"), forControlEvents: UIControlEvents.ValueChanged)
self.refreshControl = refreshControl
}
Refresh func
func refreshData(){
dispatch_async(dispatch_get_main_queue()) {
self.loadDataServer()
self.sosTableView.reloadData()
}
refreshControl?.endRefreshing()
}
load server func
func loadDataServer(){
do {
let data = NSData(contentsOfURL: NSURL(string: "http://xxxx/scripts/xxx.php")!)
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
//let NumberOfPersons = jsonResult.count
// **LOOP THROUGH THE JSON ARRAY**
for anItem in jsonResult as! [Dictionary<String, AnyObject>] {
let userId = anItem["userId"] as! String
let userName = anItem["firstName"] as! String
let userAddress = anItem["address"] as! String
let userDate = anItem["date"] as! String
let userLocation = anItem["location"] as! String
var userEvent = anItem["event"] as? String
let sosId = anItem["sosId"] as! String
// do something with personName and personID
let imageUrl = NSURL(string:"http://xxx")
let imageData = NSData(contentsOfURL: imageUrl!)
if userEvent == nil{
userEvent = "Need Help"
}else if userEvent! == "1" {
userEvent! = "Thief"
}
else if userEvent! == "2" {
userEvent! = "Fire"
}
else{
userEvent! = "Healthy Issue"
}
//print(personName)
if imageData == nil{
let photo1 = UIImage(named: "defaultPhoto")!
let data1 = dataSos(userId: userId, name: userName, location: userLocation, address: userAddress, event: userEvent!, date: userDate, photo: photo1, sosId: sosId)
self.datas += [data1]
}
else{
let photo1 = UIImage(data: imageData!)
//let photo1 = UIImage(named: "defaultPhoto")
let data1 = dataSos(userId: userId, name: userName, location: userLocation, address: userAddress, event: userEvent!, date: userDate, photo: photo1, sosId: sosId)
self.datas += [data1]
}
}
} catch let error as NSError {
print(error)
}
// }
}
Update: table view data source
// 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 datas.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MainTableViewCell
// Configure the cell...
let data0 = datas[indexPath.row]
cell.nameLabel.text = data0.name
cell.locationLabel.text = data0.location
cell.addressTextView.text = data0.address
cell.eventLabel.text = data0.event
cell.dateLabel.text = data0.date
cell.photoLabel.image = data0.photo
self.roundingUIView(cell.photoLabel, cornerRadiusParam: 35)
return cell
}
Ok, I just understood that you're inheriting for UITableViewController, therefor you already have tableView property inherited from it. The table view from this property has already set delegate and dataSource to your controller, but not for your custom sosTableView. You should replace your custom sosTableView with inherited tableView property and then everything gonna work as you're expecting.

How to implant a sectioned tableview with Parse in Swift

I am struggling to figure out how to implement a sectioned table view using parse. I am able to correctly section the table view if I don't load the names I want. However, when I use parse the names don't load in time and therefore are missed as it is async.
When I reload the table in queryFriends() it doesn't show up. My theory is that the table isn't sectioned again.
How do I make the table section again?
Or does anyone have any ideas?
Thanks for your help
import UIKit
import Parse
import Foundation
class MyFriendsTableView: UITableViewController, UITableViewDataSource, UITableViewDelegate{
var names:[String] = []
/*var names: [String] = [
"AAAA",
"Clementine",
"Bessie",
"Yolande",
"Tynisha",
"Ellyn",
"Trudy",
"Fredrick",
"Letisha",
"Ariel",
"Bong",
"Jacinto",
"Dorinda",
"Aiko",
"Loma",
"Augustina",
"Margarita",
"Jesenia",
"Kellee",
"Annis",
"Charlena",
"!##",
"###"
]*/
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl = UIRefreshControl()
self.refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
self.refreshControl?.addTarget(self, action: "queryFriends", forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(refreshControl!)
}
override func viewDidAppear(animated: Bool) {
UIApplication.sharedApplication().statusBarHidden = false
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
queryFriends()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func queryFriends() {
let currentUser = PFUser.currentUser()?.username
let predicate = NSPredicate(format: "status = 'Friends' AND fromUser = %# OR status = 'Friends' AND toUser = %#", currentUser!, currentUser!)
var query = PFQuery(className: "FriendRequest", predicate: predicate)
var friends:[String] = []
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
if object["toUser"] as? String != currentUser {
friends.append(object["toUser"] as! String)
} else if object["fromUser"] as? String != currentUser {
friends.append(object["fromUser"] as! String)
}
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
self.names = friends
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
/* type to represent table items
`section` stores a `UITableView` section */
class User: NSObject {
let name: String
var section: Int?
init(name: String) {
self.name = name
}
}
// custom type to represent table sections
class Section {
var users: [User] = []
func addUser(user: User) {
self.users.append(user)
}
}
// `UIKit` convenience class for sectioning a table
let collation = UILocalizedIndexedCollation.currentCollation()
as! UILocalizedIndexedCollation
// table sections
var sections: [Section] {
// return if already initialized
if self._sections != nil {
return self._sections!
}
// create users from the name list
var users: [User] = names.map { name in
var user = User(name: name)
user.section = self.collation.sectionForObject(user, collationStringSelector: "name")
return user
}
// create empty sections
var sections = [Section]()
for i in 0..<self.collation.sectionIndexTitles.count {
sections.append(Section())
}
// put each user in a section
for user in users {
sections[user.section!].addUser(user)
}
// sort each section
for section in sections {
section.users = self.collation.sortedArrayFromArray(section.users, collationStringSelector: "name") as! [User]
}
self._sections = sections
return self._sections!
}
var _sections: [Section]?
// table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.sections.count
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.sections[section].users.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let user = self.sections[indexPath.section].users[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = user.name
return cell
}
/* section headers
appear above each `UITableView` section */
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
// do not display empty `Section`s
if !self.sections[section].users.isEmpty {
return self.collation.sectionTitles[section] as? String
}
return ""
}
/* section index titles
displayed to the right of the `UITableView` */
override func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject] {
return self.collation.sectionIndexTitles
}
override func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
return self.collation.sectionForSectionIndexTitleAtIndex(index)
}
}
You can try to clear the _sections in queryFriends:
self._sections = nil
self.names = friends
self.tableView.reloadData()
self.refreshControl?.endRefreshing()

Drag and Drop to NSArrayController

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.

Resources