TableView custom swipe action - Rxswift - swift

Here is how i use RxSwift in my code:
My news variable in ViewModel:
var news = PublishSubject<[Article]>()
Extensions for using Rxswift with tableview.
extension HomeViewController {
func designTableView() {
newsTable.separatorStyle = .none
newsTable.showsVerticalScrollIndicator = false
}
func createCellView() {
homeViewModel.news.bind(to: newsTable.rx.items(cellIdentifier: "cell", cellType: NewsCell.self)) { _, news, cell in
cell.newsTitle.text = news.title
cell.newsImage?.sd_setImage(with: URL(string: news.urlToImage ?? ""), placeholderImage: UIImage(systemName: "photo"))
}.disposed(by: disposeBag)
}
func whenNewsSelected() {
newsTable.rx.modelSelected(Article.self).bind { article in
let vc = self.storyboard?.instantiateViewController(withIdentifier: "NewsDetail") as? NewsDetails
vc?.article = article
self.navigationController?.pushViewController(vc!, animated: true)
}.disposed(by: disposeBag)
}
func setDelegateForTableView() {
newsTable.rx.setDelegate(self).disposed(by: disposeBag)
}
}
What I want to do is, adding a Favorite action to cell. I tried the code belove but I don't how can I access the news data on that row.
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
{
let FlagAction = UIContextualAction(style: .normal, title: "Fav", handler: { (_: UIContextualAction, _: UIView, success: (Bool) -> Void) in
print("osman")
success(true)
})
FlagAction.backgroundColor = .orange
return UISwipeActionsConfiguration(actions: [FlagAction])
}
Can someone help me about this?

Instead of var news = PublishSubject<[Article]>() use let news = BehaviorSubject<[Article]>(value: []) (Note: Subjects should always be let constants, never var.)
Then when you need access to the articles, you can do:
let articles = (try? homeViewModel.news.value()) ?? []
I don't normally recommend using Subjects but the alternative here is to write your own DataSource type or use RxDataSources and that may be too deep down the rabbit hole for you.
if you do want to go down the rabbit hole
Bring in RxDataSources or write your own data source and make it a member of your view controller:
let dataSource = MyDataSource<Article, NewsCell>(cellIdentifier: "cell") { _, news, cell in
cell.newsTitle.text = news.title
cell.newsImage?.sd_setImage(with: URL(string: news.urlToImage ?? ""), placeholderImage: UIImage(systemName: "photo"))
}
use it like this:
func createCellView() {
homeViewModel.news
.bind(to: newsTable.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
Then you can access the articles like this:
let articles = dataSource.elements
Here's what a basic data source looks like. This does everything the default RxCocoa data source does.
class MyDataSource<Element, Cell>: NSObject, UITableViewDataSource, RxTableViewDataSourceType
where Cell: UITableViewCell {
private(set) var elements: [Element] = []
private let cellIdentifier: String
private let configureCell: (Int, Element, Cell) -> Void
init(cellIdentifier: String, configureCell: #escaping (Int, Element, Cell) -> Void) {
self.cellIdentifier = cellIdentifier
self.configureCell = configureCell
}
func tableView(_ tableView: UITableView, observedEvent: RxSwift.Event<[Element]>) {
guard case let .next(elements) = observedEvent else { return }
self.elements = elements
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
elements.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? Cell else {
return UITableViewCell()
}
let element = elements[indexPath.row]
configureCell(indexPath.row, element, cell)
return cell
}
}

Related

Issue in DiffableDataSource tableview header

I'm using the tableView using DiffableDataSource and only issue which I've faced in header.I'm using viewForheaderSections method for calling header header view appear but on bottom position not top of the list please see the code thanks.
extension SinlgeTableViewcontroller:UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: Header.self)) as? Header else {return UIView()}
cell.lblHeader.text = "Top Mens"
return cell
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 40
}
func createDataSource(){
dataSource = UITableViewDiffableDataSource<ProductsSections,AnyHashable>(tableView: tableView, cellProvider: { tableView, indexPath, itemIdentifier in
switch self.sectionsData[indexPath.section]{
case .first:
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ProductCell.self)) as? ProductCell else {return UITableViewCell()}
cell.products = self.products[indexPath.row]
return cell
case .second:
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MensCell.self)) as? MensCell else {return UITableViewCell()}
cell.mens = self.mens[indexPath.row]
return cell
}
})
}
func createSnapshot(){
var snapshot = NSDiffableDataSourceSnapshot<ProductsSections,AnyHashable>()
sectionsData.forEach{ $0
snapshot.appendSections([$0])
}
// snapshot.appendSections([.first,.second])
snapshot.appendItems(products, toSection: .first)
snapshot.appendItems(mens, toSection: .second)
dataSource?.apply(snapshot, animatingDifferences: true, completion: nil)
}
According to your code you are not using viewForHeaderInSection. Instead of that you are using viewForFooterInSection. So instead of a header, a footer will appear in the bottom.
If you only need a header change all the methods related to the footer to the header.

MapKit local search results populate the table

I am trying to load the updated search results but it doesn't populate the table view.
I used this link https://www.thorntech.com/how-to-search-for-location-using-apples-mapkit/ which belongs to the previous versions but it still works very well except showing the local search results. Please help
class LocationSearchTable : UITableViewController, UISearchResultsUpdating {
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
}
extension LocationSearchTable {
func updateSearchResults(for searchController: UISearchController) {
guard let MapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchBarText
request.region = MapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
print("No response")
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = ""
return cell
}
}
//use IndexPath rather than NSIndexPath and you need to use
//override
override func tableView(_ tableView: UITableView,
cellForRowAtIndexPath
indexPath: IndexPath) -> UITableViewCell {
let cell =tableView.dequeueReusableCell(withIdentifier:"cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = ""
return cell
}
Hope it is not too late to answer you!

Multiple CollectionView inside TableView Swift

I have a Tableview, I have a collectionView in it. There is imageView inside the CollectionView. As you can see in the photo at the moment, two TableViews are created separately. But the data in the CollectionViews inside this tableView is the same. Important: I have one collectionView. CollectionView is created according to the tableView Count. The collectionView's hps has the same data. I want to upload different data to CollectionViews, how can I do this? There is a well-known tableView for circumcision and weddings, but the CollectionView contents are the same. I have to do it differently.
For example, I want to add the data in davetiyefilee Array to 1.CollectionView. 2.I want to add the data from davetiyefilee2 Array to the CollectionView
enter image description here
enter image description here
UITableViewCell
class CategoryRow : UITableViewCell {
#IBOutlet weak var firstColView: UICollectionView!
var data = [String]()
var collectionData = [[String]]()
}
extension CategoryRow : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return collectionData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellDav1", for: indexPath) as! anaSayfaCell
let rowValue = collectionData[indexPath.row]
for i in 0..<rowValue.count {
let deneme = collectionData[firstColView.tag][indexPath.item]
let urlNew = URL(string: deneme)
cell.denemeImage.sd_setImage(with: urlNew)
}
return cell
}
func setData(data: [String])
{
self.data = data
self.firstColView.reloadData()
}
func setDataa(collectionData: [[String]])
{
self.collectionData = collectionData
self.firstColView.reloadData()
}
}
MainView
extension anaSayfaViewController : UITableViewDelegate { }
extension anaSayfaViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return kategoriIsımYeni[section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return kategoriIsımYeni.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") as! CategoryRow
cell.setData(data: davetiyefilee)
cell.setDataa(collectionData: collectionData)
return cell
}
}
class anaSayfaViewController: UIViewController, UISearchBarDelegate {
var collectionData = [[String]]()
var davetiyefilee = [String]()
var davetiyefilee2 = [String]()
#objc func davetiyeCEK1(){
if let baslik = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for review in baslik {
if let soru_baslik = review["davetiyefilee"] as? String {
let s = String(describing: soru_baslik)
self.davetiyefilee.append(s)
self.collectionData.append([s])
DispatchQueue.main.async { self.tableViewKategoriler.reloadData() } } } }
if let baslik = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
for review in baslik {
if let soru_baslik = review["davetiyefilee"] as? String {
let s = String(describing: soru_baslik)
self.davetiyefile2.append(s)
self.collectionData.append([s])
DispatchQueue.main.async {
self.tableViewKategoriler.reloadData()
} } } }}
Swift has a handy property to its Views called tag. You can assign a unique tag to each of your CollectionViews and then add your data based on a condition like:
if collectionView.tag == 0 {
\\ your first collection view data assignment
} else if collectionView.tag == 1 {
\\ your second collection view data assignment
collectionView here refers to the parameter given by the collectionView functions above. You can assign tags using storyboard, but it is better to do that programmatically if you have many collectionViews. A good article that covers this: https://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell-in-swift/

Problem assigning data to tableview - Swift

Although I load the data into the gifsa string array in the function, I cannot see the gifsa data in the tableView. gifsa data does not appear in tableView. The data is loading data in the veriCek() function. However, it does not load data into the imageView found in cell. What is the problem?
class NewDegisimController: UITableViewController {
var gifsa: [String] = []
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return gifsa.count
}
override func viewDidLoad() {
super.viewDidLoad()
veriCek()
}
func veriCek(){
let client = SQLClient.sharedInstance()!
client.connect("...", username: "...", password: "...", database: "...") { success in
client.execute("SELECT ... FROM ...", completion: { (_ results: ([Any]?)) in
for table in results as! [[[String:AnyObject]]] {
for row in table {
for (_, value) in row {
if let intVal = value as? String {
self.gifsa.append(String(intVal))
}} }}
client.disconnect()
}) }
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
let model = models[indexPath.row]
do {
print("gifsaas",self.gifsa)
let url = URL(string: self.gifsa[indexPath.row])
let data = try Data(contentsOf: url!)
cell.imageView?.image = UIImage(data: data)
cell.textLabel?.text = model.title
}
catch{
print(error)
}
return cell
}
You need to reload
for table in results as! [[[String:AnyObject]]] {
for row in table {
for (_, value) in row {
if let intVal = value as? String {
self.gifsa.append(String(intVal))
}} }}
DispatchQueue.main.async {
self.tableView.reloadData()
}
You just need to call
self.tableView.reload()
in your veriCek() function and you are all set

UITableView don't reload data

I got an array that populates a tableview, it works fine when I run the app.
I created a popover with a PickerView to choose one option to sort the TableView data.
I get the user choise in the popover, pass it to the main ViewController, sorted the data and called tableview.reloadData() but nothing happens.
I printed the array after the sort and the array is sorted but I can't saw the changes.
But if I go to other ViewController and came back the data is changed.
Why the changes are not showing when I call the tableview.reloadData().
Here's the code:
var dataModel = DataModel()
var ordenacao = String()
override func viewWillAppear(_ animated: Bool) {
dataModel.loadData()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
ordenadados(ordem: ordenacao)
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")
cell.textLabel?.text = dataModel.notas[indexPath.row].titulo
cell.detailTextLabel?.text = dataModel.notas[indexPath.row].datafinal
print("Ordena Tableview")
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataModel.notas.count
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
dataModel.notas.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.fade)
dataModel.saveData()
}
EDIT:
func ordenadados(ordem: String){
dataModel.loadData()
if(ordenacao == "Titulo Asc."){
print("Titulo Asc")
dataModel.notas.sort { $0.titulo! < $1.titulo! }
}else if(ordenacao == "Titulo Desc."){
print("Titulo Desc.")
dataModel.notas.sort { $0.titulo! > $1.titulo! }
}
dataModel.saveData()
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
dataModel.loadData()
tableView.reloadData()
}
In the output the array was sorted but in the TableView nothing changed.
Save and Load Data methods:
//save data
func saveData() {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(notas, forKey: "teste")
archiver.finishEncoding()
data.write(toFile: dataFilePath(), atomically: true)
}
//read data
func loadData() {
let path = self.dataFilePath()
let defaultManager = FileManager()
if defaultManager.fileExists(atPath: path) {
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
notas = unarchiver.decodeObject(forKey: "teste") as! Array
unarchiver.finishDecoding()
}
}