Scroll to the latest inserted row in UITableView using Realm Objects - ios

I have the following code which is working fine, it gets a list of items from a list in Realm called groceryList and displays them on a UITableView in descending order based on the productName. What I would like to be able to do is scroll to the latest inserted row/item in the table, right now when a new item is inserted the user may not see it since the items are alphabetically reordered and the latest item may not be visible on the tableView.
How can I scroll to the latest inserted row/item in a UITableView?
Realm Objects:
class Item:Object{
#objc dynamic var productName:String = ""
#objc dynamic var isItemActive = true
#objc dynamic var createdAt = NSDate()
}
class ItemList: Object {
#objc dynamic var listName = ""
#objc dynamic var createdAt = NSDate()
let items = List<Item>()
}
UITableView:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var allItems : Results<Item>!
var groceryList : ItemList!
override func viewDidLoad() {
super.viewDidLoad()
groceryList = realm.objects(ItemList.self).filter("listName = %#", "groceryList").first
updateResultsList()
}
func updateResultsList(){
if let list = groceryList{
allItems = activeList.items.sorted(byKeyPath: "productName", ascending: false)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! CustomCell
let data = allItems[indexPath.row]
cell.displayProductName.text = data.productName
return cell
}
}

You can use Realm notifications to know when the data source Results has been modified, then update your table view from there and do the scrolling as well.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var allItems: Results<Item>!
var groceryList: ItemList!
var notificationToken: NotificationToken? = nil
deinit {
notificationToken?.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
groceryList = realm.objects(ItemList.self).filter("listName = %#", "groceryList").first
updateResultsList()
observeGroceryList
}
func updateResultsList(){
if let list = groceryList {
allItems = activeList.items.sorted(byKeyPath: "productName", ascending: false)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reusableCell", for: indexPath) as! CustomCell
let data = allItems[indexPath.row]
cell.displayProductName.text = data.productName
return cell
}
func observeGroceryList() {
notificationToken = allItems.observe { [weak self] (changes: RealmCollectionChange) in
switch changes {
case .initial:
self?.tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
self?.tableView.beginUpdates()
self?.tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
self?.tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
self?.tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
self?.tableView.endUpdates()
if let lastInsertedRow = insertions.last {
self?.tableView.scrollToRow(at: insertions.last, at: .none, animated: true)
}
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
print("\(error)")
}
}
}
}

Add below code as extension of tableview.
extension UITableView {
func scrollToBottom() {
let sections = numberOfSections-1
if sections >= 0 {
let rows = numberOfRows(inSection: sections)-1
if rows >= 0 {
let indexPath = IndexPath(row: rows, section: sections)
DispatchQueue.main.async { [weak self] in
self?.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
}
}
}
Now simply use it in your method:
func updateResultsList(){
if let list = groceryList{
allItems = activeList.items.sorted(byKeyPath: "productName", ascending: false
yourTableView.scrollToBottom()
}
}
Just use this method where you want, it should be scroll down.
yourTableView.scrollToBottom()

Related

(Swift) How to hide some sections in tableView when toggle switch is on?

I have 5 tebleView sections, code is mostly the same for each of them. Main difference is in cell.labelCell.text:
CellForRow method:
let cell = tableView.dequeueReusableCell(withIdentifier: "ToggleTableViewCell", for: indexPath) as! ToggleTableViewCell
cell.cellSwitch.setOn(false, animated: false)
switch (indexPath.section, indexPath.row) {
case (1,0):
cell.labelCell.text = "Section 1"
cell.callback = { [unowned self] check in
UIView.transition(with: tableView,
duration: 0.5,
options: .showHideTransitionViews,
animations: { self.tableView.reloadData() })
}
cell's class:
class ToggleTableViewCell: UITableViewCell {
#IBOutlet weak var labelCell: UILabel!
#IBOutlet weak var cellSwitch: UISwitch!
var callback:((Bool) -> Void)?
#IBAction func toggleSwitch(_ sender: Any) {
if cellSwitch.isOn == true {
callback?(true)
}
else {
callback?(false)
}
}
}
Question : If I have 5 sections like this and I want to hide for example the first one when toggleSwitch is ON in the last one, is it possible to do it?
Approach:
In your Model, you can create a isHidden property that will keep a track of whether the section should be hidden or not, i.e.
class Model {
var sectionName: String
var isHidden = false
init(sectionName: String) {
self.sectionName = sectionName
}
}
Now, modify the UITableViewDataSource methods to,
class VC: UIViewController, UITableViewDataSource, UITableViewDelegate {
let arr = [Model(sectionName: "Section 1"), Model(sectionName: "Section 2"), Model(sectionName: "Section 3"), Model(sectionName: "Section 4"), Model(sectionName: "Section 5")]
lazy var dataSource = self.arr
func numberOfSections(in tableView: UITableView) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cellSwitch.setOn(false, animated: false)
cell.labelCell.text = dataSource[indexPath.section].sectionName
cell.callback = {[weak self] in
guard let `self` = self else {
return
}
self.arr[indexPath.section].isHidden = !(self.arr[indexPath.section].isHidden)
self.dataSource = self.arr.filter({ !$0.isHidden })
tableView.reloadData()
}
return cell
}
}
And you can simply call the closure callback?() in toggleSwitch(_:) and the hiding/unhiding will be handled automatically.
class TableViewCell: UITableViewCell {
#IBOutlet weak var labelCell: UILabel!
#IBOutlet weak var cellSwitch: UISwitch!
var callback:(()->())?
#IBAction func toggleSwitch(_ sender: Any) {
callback?()
}
}
I do not know what you want to display, but if the question is "Can you show/hide a section of a tableView" answer is YES
For this to work you need to separate your view with the data you want to display
// let this be declared somewhere
// assume that the top array will be the number of section and inner one will be number fo rows
var tableData: [[String]] = [["1","2","3"], ["4","5","6"], ["7","8","9"], ["10","11","12"], ["13","14","15"]]
// tableView dataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return tableData.count
}
func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int ) -> Int {
return tableData[section].count
}
func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath ) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToggleTableViewCell", for: indexPath) as! ToggleTableViewCell
cell.cellSwitch.setOn(false, animated: false)
let data = tableData[indexPath.section]
cell.labelCell.text = data[indexPath.row]
cell.callback = { [unowned self] check in
//removes the data from the selection section
// so if you remove section 2, ["7","8","9"] will be remove
// atm, it just removes but when you decide what you actually want to do then you can play around and implement a toggling action
tableData.remove(at: indexPath.section)
UIView.transition(with: tableView,
duration: 0.5,
options: .showHideTransitionViews,
animations: { self.tableView.reloadData() })
}
}

UITableView SIGBRT error when trying to "swipe left to delete " at indexpath.row

I have a simple app that populates a UITableView based on data inputed in a different ViewController. I am trying to implement the "swipe left to delete"
My problem is that this UITableView is a dropdown table view. That is when I click on one cell of the UITableView the cells open up and show me the internal cells associated with that one cells.
I think I am missing something simple as my code to delete the row does not work, it just throws a SIGBRT error. I think because maybe I trying to remove the wrong array maybe? I think it is messed up because it is a dropdown UITableView, so I am left with a bunch of extra UITableview rows?
Code to added delete button and remove selected row.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// remove the item from the data model
tableViewData.remove(at: indexPath.row)
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
Whole code in the UITableViewController is as follows:
import Foundation
import UIKit
private let reuseidentifier = "Cell"
struct cellData {
var opened = Bool()
var title = String()
var exerciseData = [String]()
var repsSetsData = [String]()
}
//here
struct Contact {
var fullname: String
var exercises : [Exercise]
}
class Exercise : NSObject , NSSecureCoding{
static var supportsSecureCoding: Bool = true
var excerciseName: String
var excerciseReps: String
var excerciseSets: String
init(Name : String, Reps : String, Sets : String) {
excerciseName = Name
excerciseReps = Reps
excerciseSets = Sets
}
func encode(with aCoder: NSCoder) {
aCoder.encode(excerciseName, forKey: "excerciseName")
aCoder.encode(excerciseReps, forKey: "excerciseReps")
aCoder.encode(excerciseSets, forKey: "excerciseSets")
}
required convenience init?(coder aDecoder: NSCoder) {
let excerciseName = aDecoder.decodeObject(forKey: "excerciseName") as! String
let excerciseReps = aDecoder.decodeObject(forKey: "excerciseReps") as! String
let excerciseSets = aDecoder.decodeObject(forKey: "excerciseSets") as! String
self.init(Name: excerciseName, Reps: excerciseReps, Sets: excerciseSets)
}
}
class ContactController: UITableViewController {
//new
var tableViewData = [cellData]()
var contacts = [Contact]()
override func viewDidLoad() {
super.viewDidLoad()
//getting data from CoreData
self.contacts = CoreDataManager.sharedInstance.retrieveDataFromCoreData()
tableView.register(UINib(nibName: "ExerciseCell", bundle: nil), forCellReuseIdentifier: "ExerciseCell")
for contact in contacts{
var exerciseData = [String]()
var repsSetsData = [String]()
for exercise in contact.exercises{
let name = exercise.excerciseName
let sets = exercise.excerciseSets
let reps = exercise.excerciseReps
exerciseData.append(name)
repsSetsData.append("Reps: " + reps + " Sets: " + sets)
}
self.tableViewData.append(cellData.init(opened: false, title: contact.fullname, exerciseData:exerciseData, repsSetsData: repsSetsData))
}
self.tableView.reloadData()
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.title = "Workouts"
view.backgroundColor = .white
tableView.register(UITableViewCell.self, forCellReuseIdentifier: reuseidentifier)
}
#IBAction func handleAddContact(_ sender: Any) {
let controller = AddContactController()
controller.delegate = self
self.present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
}
//UITABLEVIEW
//all new
override func numberOfSections(in tableView: UITableView) -> Int {
//new
return tableViewData.count
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
//new
if tableViewData[section].opened == true {
return tableViewData[section].exerciseData.count + 1
}else {
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseidentifier, for: indexPath)
cell.textLabel?.text = tableViewData[indexPath.section].title
return cell
}else {
//use a different cell identifier if needed
let cell = tableView.dequeueReusableCell(withIdentifier: "ExerciseCell", for: indexPath) as! ExerciseCell
cell.exerciseLabel.text = tableViewData[indexPath.section].exerciseData[indexPath.row - 1]
cell.repsSetsLabel.text = tableViewData[indexPath.section].repsSetsData[indexPath.row - 1]
cell.repsSetsLabel.sizeToFit()
return cell
}
}
//did select row new
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewData[indexPath.section].opened == true {
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none) //play around with animation
}else {
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none) //play around with animation
}
}
//being able to delete a row
// this method handles row deletion
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// remove the item from the data model
tableViewData.remove(at: indexPath.row)
// delete the table view row
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
}
//this is an extention to addContactController. this is what happens whent he done button is clicked in addcontactcontroller
extension ContactController: AddContactDelegate {
func addContact(contact: Contact) {
self.dismiss(animated: true) {
//Saving Data to CoreData
CoreDataManager.sharedInstance.addContactsToCoreData(contact: contact)
self.contacts.append(contact)
//Settings values in table view
var exerciseData = [String]()
var repsSetsData = [String]()
for exercise in contact.exercises{
let name = exercise.excerciseName
let sets = exercise.excerciseSets
let reps = exercise.excerciseReps
exerciseData.append(name)
repsSetsData.append("Reps: " + reps + " Sets: " + sets)
}
self.tableViewData.append(cellData.init(opened: false, title: contact.fullname, exerciseData:exerciseData, repsSetsData: repsSetsData))
self.tableView.reloadData()
}
}
}
After deleting the rows can you try to reload the tableview cells like this: self.tableView.reloadData()
It's supposed that you delete a row not an entire section , so replace
tableViewData.remove(at: indexPath.row)
with
tableViewData[indexPath.section].exerciseData.remove(at: indexPath.row)
also make sure exerciseData is mutable ( declared as var )

UITableView reload issue

I have UITableView inside UIScrollView and implemented paging with which I get 10 records in each page. I am facing a problem when after the IndexPath row is 9 then again UITableView reloads cells starting from row 2 due to which all the Pages are loaded once. Here is my code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if listData != nil{
print("list count in numberOfRowsInSection\(listData?.count)")
return (listData?.count)!
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("CellForRowIndexPath:\(indexPath.row)")
let cell: NewPostCell = tableView.dequeueReusableCell(withIdentifier: "NewPostCell") as? NewPostCell ??
NewPostCell(style: .default, reuseIdentifier: "NewPostCell")
cell.delegate = self
cell.updateWithModel(self.listData![indexPath.row] as AnyObject)
cell.tbleUpdateDelegate = self
cell.selectionStyle = .none
cell.accessoryType = .none
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
print("in will display row - \(indexPath.row)")
if pageNumber >= totalPages {
return
}
else
{
if (listData?.count)! == 10*pageNumber{
if (listData?.count)! - 3 == indexPath.row{
if !boolHitApi{
boolHitApi = true
return
}
pageNumber += 1
self.callService()
}
}
}
}
override func viewWillAppear(_ animated: Bool) {
callService()
}
func callService(){
SVProgressHUD.show()
setPageToOne()
ProfileApiStore.shared.requestToGetProfile(loggedInUserId: UserStore.shared.userId, userId: UserStore.shared.userIdToViewProfile, limit: "10", page: String(self.pageNumber), completion: {(result) in
SVProgressHUD.dismiss()
self.totalPages = result.totalPages!
if self.listData?.count == 0 || (self.pageNumber as AnyObject) as! Int == (1 as AnyObject) as! Int{
self.listData = result.userdata?.newPostData
} else {
self.listData = self.listData! + (result.userdata?.newPostData)!
}
self.tableView.reloadData()
})
}
The code you mentioned in the comments adds 10 rows to the datasource (self.listData) but you are only calling insertRows with one row.
You could loop through them, adding an item to the array and adding a row each time:
func callService() {
ProfileApiStore.shared.requestToGetProfile(loggedInUserId: UserStore.shared.userId, userId: UserStore.shared.userIdToViewProfile, limit: "10", page: String(self.pageNumber), completion: {(result) in
SVProgressHUD.dismiss()
self.totalPages = result.totalPages!
let newItems = result.userdata?.newPostData
tableView.beingUpdates()
for item in newItems {
self.listData.append(item)
let indexPath = IndexPath(row:(self.listData!.count-1), section:0) // shouldn't this be just self.listData!.count as your adding a new row?
tableView.insertRows(at: [indexPath], with: .left)
}
tableView.endUpdates()
})
}

RLMException when using Realm through different view controllers

I am using Realm in swift to create a list of favorite products to be saved on the user's device.
In short, 2 views controllers (A and B) are embeded in a tab bar controller.
View controller A displays some products that are fetched from a Firebase database.
View controller A also has a "add to favorites" button that adds a product to a Realm instance.
View controller B embeds a table view to display the results from this Realm instance.
The problem is that when I delete some products from the table view, I get
the following error as soon as I go back to view controller A.
Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated
I have read that this error occurs when trying to access deleted objects from Realm, or when trying to re-add an object that was previously deleted. That doesn't seem to be the case here.
Thank you for any advice.
Here is the "add to favorite" button in my View Controller A:
#IBAction func addToFavorites(_ sender: Any) {
let realm = try! Realm()
try! realm.write {
realm.add(currentProduct)
}
}
Here is the table view code in my View Controller B:
class ViewControllerB: UIViewController, UITableViewDataSource, UITableViewDelegate {
let realm = try! Realm()
var favorites: Results<Product>!
#IBOutlet weak var favoritesTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
favoritesTableView.delegate = self
favoritesTableView.dataSource = self
favoritesTableView.rowHeight = 70
}
override func viewWillAppear(_ animated: Bool) {
favorites = realm.objects(Product.self)
favoritesTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favorites.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! favoriteTableViewCell
let product = favorites[indexPath.row]
cell.productName.text = product.name
cell.productCategory.text = product.category
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let product = favorites[indexPath.row]
try! realm.write {
realm.delete(product)
}
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom)
}
}
EDIT:
Here is the full view controller A
import UIKit
import RealmSwift
class ViewControllerA: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var products = [Product]()
var scannedProduct = Product()
var currentProduct = Product()
let realm = try! Realm()
#IBOutlet weak var myCollectionView: UICollectionView!
#IBAction func addToFavorites(_ sender: Any) {
try! realm.write {
realm.add(currentProduct)
}
}
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.delegate = self
myCollectionView.dataSource = self
myCollectionView.isPagingEnabled = true
myCollectionView.backgroundColor = UIColor.blue
layout()
}
override func viewDidAppear(_ animated: Bool) {
currentProduct = scannedProduct
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return products.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! collectionCell
cell.productName.text = products[indexPath.item].name
cell.codeLabel.text = products[indexPath.item].code
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: myCollectionView.bounds.width, height: myCollectionView.bounds.height)
}
func layout() {
if let flowLayout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let currentVisibleCell = myCollectionView.visibleCells.first
let currentIndex = myCollectionView.indexPath(for: currentVisibleCell!)?.item
currentProduct = products[currentIndex!]
}
}
when trying to access deleted objects from Realm, That doesn't seem to be the case here.
Actually...
try! realm.write {
realm.delete(product)
}
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom)
These two lines shouldn't look like this. You see, Realm has this thing called a NotificationToken, and the ability to observe a results. According to the examples, it should look like this:
var notificationToken: NotificationToken?
override func viewDidLoad() {
...
// Set results notification block
self.notificationToken = results.observe { (changes: RealmCollectionChange) in
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
self.tableView.reloadData()
break
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the TableView
self.tableView.beginUpdates()
self.tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
self.tableView.endUpdates()
break
case .error(let err): // this is probably not relevant in a non-sync scenario
break
}
}
If you have this, then you can theoretically just remove your own tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.bottom) calls. This observer would keep the TableView up to date whenever a change occurs (write happens to Realm).

error occured after tableView.endUpdates()

I tried to put a breakpoint and I noticed that error occured after the tableView.endUpdates
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 0. The number of rows contained in an
existing section after the update (333) must be equal to the number of
rows contained in that section before the update (333), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
235 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'
import UIKit
import RealmSwift
import RxCocoa
import RxSwift
var myIndex = 0
class TCB_View: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate{
var notificationToken: NotificationToken? = nil
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var filteredArray:[String] = []
var branchesArray:[String] = []
var compositeDisposable = CompositeDisposable()
var isSearchin = false
deinit {
compositeDisposable.dispose()
notificationToken?.invalidate()
}
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView?.delegate = self
tableView?.dataSource = self
searchBar?.delegate = self
searchBar?.returnKeyType = UIReturnKeyType.done
searchBar?.placeholder = "Search Trial Courts"
let realm = try! Realm()
let tcb = realm.objects(TrialCourtBranches.self).filter("tc.tc_filter == true")
count = tcb.count
print(count)
for branch in tcb{
branchesArray.append(branch.branch_name)
}
notificationToken = tcb.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
let fromRow = {(row: Int) in return IndexPath(row: row,section:0) }
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map(fromRow),with: .automatic)
tableView.deleteRows(at: deletions.map(fromRow),with: .automatic)
tableView.reloadRows(at: modifications.map(fromRow),with: .automatic)
tableView.endUpdates()
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == ""{
isSearchin = false
view.endEditing(true)
tableView.reloadData()
}else{
isSearchin = true
compositeDisposable.insert(UISearchBar.rx.init(searchBar).value.subscribe(onNext: { (stringResult) in
self.filteredArray = self.branchesArray.filter({(branchesArray:String) -> Bool in
if branchesArray.contains(self.searchBar.text!){
return true
}else{
return false
}
})
}, onError: { (errorResult) in
print(errorResult)
}, onCompleted: {
print("onCompleted")
}, onDisposed: {
print("onDisposed")
}))
tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TrialCourtCell") as! CustomTableViewCell
cell.branchName.text = branchesArray[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex = indexPath.row
}
}
When I tapped the tableCell in my other viewcontroller , error execute

Resources