How to implant a sectioned tableview with Parse in Swift - xcode

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

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.

Insert information from REST API in a table view

I after asking another question today and fixed my Swift 4.2 code, I realized that I can't debug the app because of some profiles.
Errors shown when Testing application
The app also crashes on launch so there is not much I can do for now. I believe that the issue is how I am getting the information from the webserver (currently my computer). I am quite new to this, so i might have some mistakes in my code, so bear with me.
import UIKit
class InfoTableViewController: UITableViewController {
//MARK: Properties
class Produs {
var nume: String!
var codBare: String!
init(nume: String, codBare: String) {
self.nume = nume
self.codBare = codBare
}
}
var produse = [Produs]()
override func viewDidLoad() {
super.viewDidLoad()
//Load elements from server, theoretically
loadProducts()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return produse.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "InfoTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? InfoTableViewCell else {
fatalError("The dequeued cell is not an instance of InfoTableViewCell.")
}
// Fetches the appropriate meal for the data source layout.
let produs = produse[indexPath.row]
cell.nameMain.text = produs.nume
cell.nameInfo.text = produs.codBare
return cell
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
//MARK: Private Methods
private func loadProducts() { //This function retrieves information in a JSON format from the server
var request = URLRequest(url: URL(string: "192.168.0.145")!)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request, completionHandler: { data, response, error -> Void in
do {
let decoder = JSONDecoder()
let json = try decoder.decode([[String]].self, from: data!)
print(json)
for produs in json {
print(produs)
var nume_prod: String = produs[0] // Numele produsului
var cod_ext: String = produs[1] // Codul de bare al produsului
var prod_obj = Produs(nume: nume_prod, codBare: cod_ext)
self.produse.append(prod_obj)
}
} catch {
print("JSON Serialization error")
}
}).resume()
}
}
I am getting the information from the server in a JSON file that is an array of arrays and looks like this:
[
[
"product1",
"code1"
],
[
"product2",
"code2"
],
[
"product3",
"code3"
]
]
Thank you for your help!
Don't send the JSON as array of arrays, send it as array of dictionaries. It simplifies the decoding considerably.
[
{"name":"product1", "code":"code1"},
{"name":"product2", "code":"code2"},
{"name":"product3", "code":"code3"}
]
Then declare the model as struct and never declare properties as implicit unwrapped optional which are initialized with non-optional values. If you need optionals declare them as regular optional (?) otherwise non-optional
struct Produs {
let name: String
let code: String
}
Replace loadProducts with
private func loadProducts() { //This function retrieves information in a JSON format from the server
let url = URL(string: "192.168.0.145")!
URLSession.shared.dataTask(with: url, completionHandler: {[unowned self] data, response, error in
if let error = error { print(error); return }
do {
self.produse = try JSONDecoder().decode([Produs].self, from: data!)
print(self.produse)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}).resume()
}
And replace cellForRowAt with
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "InfoTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! InfoTableViewCell
// Fetches the appropriate meal for the data source layout.
let produs = produse[indexPath.row]
cell.nameMain.text = produs.name
cell.nameInfo.text = produs.code
return cell
}

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.

Parse.com - Download Objects From Database - Show Progress With ProgressBlock

I have Parse class called Product that has 238 rows. Note that this class is not the Parse.com implementation of Product, it is a custom class implemented by myself, as I didn't require all the columns Parse adds to their Product class.
The Product class has a Pointer column (basically a foreign key in SQL tables), called ShopId, because each product belongs to a specific Shop (I have a Parse class called Shop with an ObjectId column used in the Product Pointer.
My Product class also has a File column called imageFile that holds the image of the product.
I want to download all Products from a specific shop, unpackage their image file and put it in my Swift Product class which consists of the PFObject of the Parse Product, and a UIImageView and a UIImage. Here is my Product Class in Swift:
class Product {
private var object: PFObject
private var imageView: MMImageView!
private var image: UIImage
init(object: PFObject, image: UIImage) {
self.object = object
self.image = image
}
func getName() -> String {
if let name = object["name"] as? String {
return name
} else {
return "default"
}
}
func setImageView(size: CGFloat, target: DressingRoomViewController) {
self.imageView = MMImageView(frame:CGRectMake(0, 0, size, size))
imageView.contentMode = UIViewContentMode.ScaleAspectFit
imageView.image = self.image
imageView.setName(object["category"] as! String)
imageView.backgroundColor = UIColor.clearColor()
imageView.userInteractionEnabled = true
let tapGestureRecognizer =
UITapGestureRecognizer(target: target, action: "imageTapped:")
tapGestureRecognizer.numberOfTapsRequired = 1
imageView.addGestureRecognizer(tapGestureRecognizer)
}
func getImageView() -> MMImageView {
return self.imageView
}
}
I am currently downloading all the products just fine, and getting their image file and creating my Swift Products with their images. However my UIProgressView logic is slightly off. I have the UIProgressView running for every product, every time I unpackage the product image. I need to shift the Parse.com ProgressBlock out of the getProduct swift function and into the loadProducts #IBAction. When I try it, it causes a lot of errors before compilation. How do I shift the ProgressBlock up to the loadProducts #IBAction? Here is my current code:
//
// ChooseShopViewController.swift
// MirrorMirror
//
// Created by Ben on 12/09/15.
// Copyright (c) 2015 Amber. All rights reserved.
//
import UIKit
import Parse
class ChooseShopViewController: UIViewController {
var progressView: UIProgressView?
private var allProducts: [Product] = []
private var categories: [ProductCategory] = []
#IBAction func loadProducts(sender: AnyObject) {
let shopQuery = PFQuery(className:"Shop")
shopQuery.getObjectInBackgroundWithId("QjSbyC6k5C") {
(glamour: PFObject?, error: NSError?) -> Void in
if error == nil && glamour != nil {
let query = PFQuery(className:"Product")
query.whereKey("shopId", equalTo: glamour!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
self.getAllProductsAndCategories(objects, error: error)
}
} else {
print(error)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Create Progress View Control
progressView = UIProgressView( progressViewStyle:
UIProgressViewStyle.Default)
progressView?.center = self.view.center
view.addSubview(progressView!)
}
override func prepareForSegue( segue: UIStoryboardSegue,
sender: AnyObject?) {
if (segue.identifier == "dressingRoom") {
ShopDisplay.sharedInstance.setAllProducts(self.allProducts)
ShopDisplay.sharedInstance.setAllProductCategories(self.categories)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getAllProductsAndCategories(objects: [AnyObject]?, error: NSError?) {
if error == nil {
if let objects = objects as? [PFObject] {
for product in objects {
self.getCategory(product)
self.getProduct(product)
}
}
} else {
print("Error: \(error!) \(error!.userInfo)")
}
}
func getCategory(product: PFObject) {
if let category = product["category"] as? String {
var alreadyThere: Bool = false
for item in self.categories {
if category == item.rawValue {
alreadyThere = true
break
}
}
if alreadyThere == false {
self.categories.append(ProductCategory(rawValue: category)!)
}
}
}
func getProduct(product: PFObject) {
if let productImage = product["imageFile"] as? PFFile {
productImage.getDataInBackgroundWithBlock ({
(imageData: NSData?, error: NSError?) -> Void in
if let imageData = imageData {
let image = UIImage(data:imageData)
self.allProducts.append(
Product(object: product, image: image!))
}
if let downloadError = error {
print(downloadError.localizedDescription)
}
}, progressBlock: {
(percentDone: Int32) -> Void in
self.progressView?.progress = Float(percentDone)
if (percentDone == 100) {
//self.performSegueWithIdentifier("dressingRoom", sender: UIColor.greenColor())
}
})
}
}
}
I decided to not use the progressBlock, and instead to update my UIProgressView manually with a calculation. So here is the code. It's a little rusty. I could refactor now and maybe implement a calculated variable to make it cleaner. If my solution is a bad practice then I'm appreciative if that gets pointed out, and a better solution suggested (It doesn't seem good for performance to check the UIProgressView.progress value every iteration to perform the completion task of performing the segue).
import UIKit
import Parse
class ChooseShopViewController: UIViewController {
var progressView: UIProgressView?
private var allProducts: [Product] = []
private var categories: [ProductCategory] = []
static var numberOfProducts: Float = 0
#IBAction func loadProducts(sender: AnyObject) {
let shopQuery = PFQuery(className:"Shop")
shopQuery.getObjectInBackgroundWithId("QjSbyC6k5C") {
(glamour: PFObject?, error: NSError?) -> Void in
if error == nil && glamour != nil {
let query = PFQuery(className:"Product")
query.whereKey("shopId", equalTo: glamour!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
ChooseShopViewController.numberOfProducts =
Float((objects?.count)!)
print(ChooseShopViewController.numberOfProducts)
self.getAllProductsAndCategories(objects, error: error)
}
} else {
print(error)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Create Progress View Control
progressView = UIProgressView( progressViewStyle:
UIProgressViewStyle.Default)
progressView?.center = self.view.center
progressView?.progress = 0.00
view.addSubview(progressView!)
}
override func prepareForSegue( segue: UIStoryboardSegue,
sender: AnyObject?) {
if (segue.identifier == "dressingRoom") {
ShopDisplay.sharedInstance.setAllProducts(self.allProducts)
ShopDisplay.sharedInstance.setAllProductCategories(self.categories)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getAllProductsAndCategories(objects: [AnyObject]?, error: NSError?) {
if error == nil {
if let objects = objects as? [PFObject] {
for product in objects {
self.getCategory(product)
self.getProduct(product)
}
}
} else {
print("Error: \(error!) \(error!.userInfo)")
}
}
func getCategory(product: PFObject) {
if let category = product["category"] as? String {
var alreadyThere: Bool = false
for item in self.categories {
if category == item.rawValue {
alreadyThere = true
break
}
}
if alreadyThere == false {
self.categories.append(ProductCategory(rawValue: category)!)
}
}
}
func getProduct(product: PFObject) {
if let productImage = product["imageFile"] as? PFFile {
productImage.getDataInBackgroundWithBlock ({
(imageData: NSData?, error: NSError?) -> Void in
if let imageData = imageData {
let image = UIImage(data:imageData)
self.allProducts.append(
Product(object: product, image: image!))
self.progressView?.progress += (100.00 /
ChooseShopViewController.numberOfProducts) / 100.00
print(self.progressView?.progress)
if self.progressView?.progress == 1 {
self.performSegueWithIdentifier("dressingRoom",
sender: UIColor.greenColor())
}
}
if let downloadError = error {
print(downloadError.localizedDescription)
}
})
}
}
}
I found this on the Parse website. It may be useful as it has a block that shows the percentage done that updates regularly during the download!
let str = "Working at Parse is great!"
let data = str.dataUsingEncoding(NSUTF8StringEncoding)
let file = PFFile(name:"resume.txt", data:data)
file.saveInBackgroundWithBlock({
(succeeded: Bool, error: NSError?) -> Void in
// Handle success or failure here ...
}, progressBlock: {(percentDone: Int32) -> Void in
// Update your progress spinner here. percentDone will be between 0 and 100.
})
Did you find a better solution? besides this? I am trying to do something similar.

Reordering Realm.io data in tableView with Swift

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

Resources