Set Button Title not showing on UI in Swift - swift

What I want to achieve?
I have 2 UITableViewController : AddCardVC and ListDeckController. I want to pass selected Deck in ListDeckController to AddCardVC then updateUI on AddCardVC
My code: (I'm building on Swift 5, iOS 13)
ListDeckController.swift:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let addCardVC = storyboard.instantiateViewController(withIdentifier: "AddCard") as? AddCardVC {
if let deck = allDecks?[indexPath.row] {
addCardVC.selectedDeck = deck
}
}
self.navigationController?.popViewController(animated: true)
}
AddCardVC.swift:
var selectedDeck: Deck?
{
didSet {
viewWillAppear(true)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let deck = selectedDeck {
//Update UI
self.chooseDeckButton.setTitle(deck.name, for: .normal)
print(chooseDeckButton.titleLabel?.text ?? "")
self.tableView.reloadData()
}
}
What happend?
After I got selected Deck in AddCardVC, the console print out chooseDeckButton.titleLabel?.text = "My Deck" which is what I want. But The button title on UI is "Choose your deck"
Console image Button on UI
So, why the setButtonTitle not showing on UI in my case?
Thank you for your time

First, create a protocol for DeckDelegate
protocol SelectedDeckDelegate {
func didSelectDeck(selectedDeck : Deck)
}
Now in your ListDeckController.swift, implement the delegate
var deckDelegate : SelectedDeckDelegate?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if let deck = allDecks?[indexPath.row] {
deckDelegate?.didSelectDeck(selectedDeck: deck)
self.navigationController?.popViewController(animated: true)
} else {
print("deck is empty")
}
}
And in your AddCardVC, when navigating to ListDeckController, assign the delegate to self
// Make AddCardVC conform to SelectedDeckDelegate
class AddCardVC : UIViewController, SelectedDeckDelegate {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let deck = selectedDeck {
//Update UI
self.chooseDeckButton.setTitle(deck.name, for: .normal)
print(chooseDeckButton.titleLabel?.text ?? "")
self.tableView.reloadData()
}
}
// assign the delegate to self here
#IBAction func selectDeckBtnAction(_ sender : Any) {
let vc = storyboard.instantiateViewController(withIdentifier: "ListDeckController") as? ListDeckController
vc.deckDelegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
// this will get called whenever selectedDeck is updated from ListDeckController
func didSelectDeck(selectedDeck: Deck) {
self.selectedDeck = selectedDeck
chooseDeckButton.setTitle(selectedDeck.name , for: .normal)
}
}

Related

How to show one tableview data in two segments in swift

I am using XLPagerTabStrip for segment
i want to show one tableview data in two segment with sorted, for that i am using below code
SegmentViewController code: if i add child_2.isSorted = false then error
Value of type 'UIViewController' has no member 'isSorted'
import UIKit
import XLPagerTabStrip
class SegmentViewController: ButtonBarPagerTabStripViewController {
#IBOutlet weak var testSeg: ButtonBarView!
override func viewDidLoad() {
super.viewDidLoad()
// change selected bar color
// Sets the background colour of the pager strip and the pager strip item
settings.style.buttonBarBackgroundColor = .white
settings.style.buttonBarItemBackgroundColor = .yellow
// Sets the pager strip item font and font color
settings.style.buttonBarItemFont = UIFont(name: "Helvetica", size: 15.0)!
settings.style.buttonBarItemTitleColor = .gray
changeCurrentIndexProgressive = { [weak self] (oldCell: ButtonBarViewCell?, newCell: ButtonBarViewCell?, progressPercentage: CGFloat, changeCurrentIndex: Bool, animated: Bool) -> Void in
guard changeCurrentIndex == true else { return }
oldCell?.label.textColor = .gray
newCell?.label.textColor = .blue
}
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.buttonBarView.frame = testSeg.frame
}
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
let child_1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
let child_2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
child_2.isSorted = false
return [child_1, child_2]
}
}
SegTableViewController code: here in both segments showing only sortedArray i need in first segment sortedArray and second segment namesArray.. how to do that.. could anyone please suggest me
import UIKit
import XLPagerTabStrip
class SegTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, IndicatorInfoProvider {
var sortedArray = [String]()
var isSorted = true
func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
if isSorted{
return IndicatorInfo(title: "Sorted")
}else{
return IndicatorInfo(title: "Normal")
}
}
#IBOutlet weak var tableView: UITableView!
var namesArray = ["afsdf","ddsfsdf", "hjhgjh", "trytryr", "nvbmvnb", "yuertyri", "bmvmncb", "jgfhk", "ytuioi", "sdfgsfdsh", "mkhjgijik", "gfuyru"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
sortedArray = namesArray.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedDescending }
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSorted {
return sortedArray.count
}
else{
return namesArray.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
if isSorted{
cell.textLabel?.text = sortedArray[indexPath.row]
}
else{
cell.textLabel?.text = namesArray[indexPath.row]
}
return cell
}
}
You need to cast your view controller like this
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
let child_1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
let child_2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegTableViewController")
(child_2 as? SegTableViewController).isSorted = false
return [child_1, child_2]
}
Also, no need for an array for displaying data. Use only one array namesArray like this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if isSorted {
namesArray = namesArray.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedDescending }
}
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}

how to call/show action sheet for taking or choose photo from gallery on Main VC after tapped on TableViewCell Image(swift)

Here I Try MVVM pattern to achieve TableView but for showing alert I face problems , it compiles successfully but not showing alert.
[Result] on the tapped on profile pic which is in tableview cell, I want to show alert
TableViewCell
import Foundation
import UIKit
class ParentsImageCell : UITableViewCell {
weak var myVC : ProfileOfParentsDetailsViewController?
var parentProfileVC = ProfileOfParentsDetailsViewController()
#IBOutlet weak var imageProfile : UIImageView!
var items : ParentProfileViewModelItem? {
didSet {
guard let items = items as? ParentProfileViewModelProfileItem else {
return
}
imageProfile?.image = UIImage(named: items.profileImg)
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func awakeFromNib() {
super.awakeFromNib()
imageProfile?.layer.cornerRadius = 62
imageProfile?.clipsToBounds = true
imageProfile?.contentMode = .scaleAspectFill
imageProfile?.backgroundColor = UIColor.lightGray
//Add Tapped Gesture
imageProfile.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(
target: self,
action: #selector(didTappedChangeProfilePic))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
imageProfile.addGestureRecognizer(gesture)
}
#objc private func didTappedChangeProfilePic(){
print("tapped on imageView")
presentPhotoActionSheet()
}
override func prepareForReuse() {
super.prepareForReuse()
imageProfile?.image = nil
}
}
extension ParentsImageCell : UIImagePickerControllerDelegate ,UINavigationControllerDelegate {
func presentPhotoActionSheet(){
let actionSheet = UIAlertController(title: "Profile Picture", message: "How would you write to select a picture", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self]_ in
self?.presentPhotoPicker()
}))
myVC?.present(actionSheet , animated: true)
}
func presentCamera(){
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func presentPhotoPicker(){
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:
[UIImagePickerController.InfoKey : Any]) {
guard let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
self.imageProfile.image = selectedImage
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ViewModel
class ParentProfileViewModel: NSObject {
var items = [ParentProfileViewModelItem]()
var reloadSections: ((_ section: Int) -> Void)?
override init() {
super.init()
guard let data = dataFromFile("ServerData"),
let profile = Profile(data: data) else {
return
}
// initialization code will go here
if let profile = profile.pictureUrl {
let profileItem = ParentProfileViewModelProfileItem(profileImg: profile)
items.append(profileItem)
}
if let name = profile.fullName {
let nameItem = ParentProfileViewModelNameItem(name: name)
items.append(nameItem)
}
if let email = profile.email {
let emailItem = ParentProfileViewModelEmailItem(email: email)
items.append(emailItem)
}
let coach = profile.coach
if !coach.isEmpty {
let coachItem = ParentProfileViewModelCoachItem(coach: coach)
items.append(coachItem)
}
let candidate = profile.candidate
if !candidate.isEmpty {
let candidateItem = ParentProfileViewModelCandidateItem(candidate: candidate)
items.append(candidateItem)
}
}
}
//MARK:- TableviewDatasource & Delegates
extension ParentProfileViewModel: UITableViewDataSource {
//Number of section
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
//Number of RowInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let item = items[section]
guard item.isCollapsible else {
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
return item.rowCount
}
}
//Cell for row at indexpath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// we will configure the cells here
let item = items[indexPath.section]
switch item.type {
case .profileImg:
let vc = ProfileOfParentsDetailsViewController()
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsImageCell.identifier, for: indexPath) as? ParentsImageCell {
cell.items = item
cell.myVC = vc
return cell
}
case .fullName:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsFulNameCell.identifier, for: indexPath) as? ParentsFulNameCell {
cell.items = item
return cell
}
case .email:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsEmailCell.identifier, for: indexPath) as? ParentsEmailCell {
cell.items = item
return cell
}
case .candidate:
if let item = item as? ParentProfileViewModelCandidateItem, let cell = tableView.dequeueReusableCell(withIdentifier: CandidatCell.identifier, for: indexPath) as? CandidatCell {
let candidate = item.candidate[indexPath.row]
cell.item = candidate
return cell
}
case .coach:
if let item = item as? ParentProfileViewModelCoachItem, let cell = tableView.dequeueReusableCell(withIdentifier: ParentCoachCell.identifier, for: indexPath) as? ParentCoachCell {
cell.item = item.coach[indexPath.row]
return cell
}
}
return UITableViewCell()
}
}
ViewController
import Foundation
import UIKit
class ProfileOfParentsDetailsViewController: UIViewController {
let viewModel = ParentProfileViewModel()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
viewModel.reloadSections = { [weak self] (section: Int) in
self?.tableView?.beginUpdates()
self?.tableView?.reloadSections([section], with: .fade)
self?.tableView?.endUpdates()
}
tableView?.dataSource = viewModel
tableView.delegate = viewModel
tableView?.estimatedRowHeight = 250
tableView?.rowHeight = UITableView.automaticDimension
tableView?.register(ParentsImageCell.nib, forCellReuseIdentifier: ParentsImageCell.identifier)
tableView?.register(ParentsEmailCell.nib, forCellReuseIdentifier: ParentsEmailCell.identifier)
tableView?.register(ParentsFulNameCell.nib, forCellReuseIdentifier: ParentsFulNameCell.identifier)
tableView?.register(CandidatCell.nib, forCellReuseIdentifier: CandidatCell.identifier)
tableView?.register(ParentCoachCell.nib, forCellReuseIdentifier: ParentCoachCell.identifier)
tableView?.register(ParentsHeaderView.nib, forHeaderFooterViewReuseIdentifier: ParentsHeaderView.identifier)
}
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM

How to inject presenter to CustomTableViewCell as dependancy injection

i'm learning Dependency Injection and created an app by MVP.
I could inject presenters for a few VCs in AppDelegate using storyboard.
But now I created CustomTableViewCell with .xib that has UIButton on the cell so that I would like to process the detail of it in the presenter file.
I tried to create UINib(nibName~ in AppDelegate but when the presenter in CustomTableViewCell is called, it is nil.
I heard that an instance of a cell called in 'AppDelegate' is going to be disposed of. However I don't know any ways not to make presenter nil.
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let mainTabVC = UIStoryboard(name: "MainTab", bundle: nil).instantiateInitialViewController() as! MainTabViewController
let userDefault = UserDefault()
let presenter = MainTabPresenter(view: mainTabVC, udManager: userDefault)
mainTabVC.inject(presenter: presenter, userDefaultManager: userDefault)
let addTabVC = UIStoryboard(name: "AddTab", bundle: nil).instantiateInitialViewController() as! AddTabViewController
let alertHandller = AlertHandller()
let addPresenter = AddTabPresenter(view: addTabVC, mainView: mainTabVC, alertHandller: alertHandller)
addTabVC.inject(presenter: addPresenter)
let settingTabVC = UIStoryboard(name: "SettingTab", bundle: nil).instantiateInitialViewController() as! SettingTabViewController
let settingPresenter = SettingTabPresenter(view: settingTabVC)
settingTabVC.inject(presenter: settingPresenter, alertHandller: alertHandller)
let vcs = [mainTabVC, addTabVC, settingTabVC]
let mainTabBar = UIStoryboard(name: "MainView", bundle: nil).instantiateInitialViewController() as! MainTabBarController
mainTabBar.setViewControllers(vcs, animated: false)
// no probs until here (inject functions work)
// this is disposed??
let listCell = UINib(nibName: "ListCell", bundle: nil).instantiate(withOwner: ListCell.self, options: nil).first as! ListCell
let cellPresenter = ListCellPresenter(view: mainTabVC)
listCell.inject(presenter: cellPresenter)
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = mainTabBar
window?.makeKeyAndVisible()
return true
}
VC that tableViewDataSource is written
class MainTabViewController: UIViewController {
private let shared = Sharing.shared
private var presenter: MainTabPresenterInput!
private var userDefaultManager: UserDefaultManager!
func inject(presenter: MainTabPresenterInput, userDefaultManager: UserDefaultManager) {
self.presenter = presenter
self.userDefaultManager = userDefaultManager
}
override func viewDidLoad() {
super.viewDidLoad()
tableViewSetup()
}
func tableViewSetup(){
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "ListCell", bundle: nil), forCellReuseIdentifier: "ListCell")
}
}
extension MainTabViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return shared.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! ListCell
if let item = presenter.item(row: indexPath.row) {
cell.configure(item: item)
}
return cell
}
}
customTableViewCell
class ListCell: UITableViewCell {
private let shared = Sharing.shared
private var presenter: ListCellPresenterInput!
func inject(presenter: ListCellPresenterInput) {
self.presenter = presenter
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func favButtonTapped(_ sender: Any) {
let row = self.tag
switch shared.items[row].fav {
case true:
favButton.setImage(UIImage(named: "favIconNon"), for: .normal)
case false:
favButton.setImage(UIImage(named: "favIcon"), for: .normal)
}
presenter.countFavIcon(rowAt: row) // this presenter is nil
}
func configure(item: Item) {
    ・・・
}
}
cell presenter
protocol AddTabPresenterInput {
func addButtonTapped(item item: Item?)
}
protocol AddTabPresenterOutput: AnyObject {
func clearFields()
func showAlert(alert: UIAlertController)
}
final class AddTabPresenter: AddTabPresenterInput {
private weak var view: AddTabPresenterOutput!
private weak var mainView: MainTabPresenterOutput!
private weak var alertHandller: AlertHandllerProtocol!
let shared = Sharing.shared
init(view: AddTabPresenterOutput, mainView: MainTabPresenterOutput, alertHandller: AlertHandllerProtocol) {
self.view = view
self.mainView = mainView
self.alertHandller = alertHandller
}
func addButtonTapped(item item: Item?) {
print("called")
mainView.updateView()
}
}
How Can I solve the issue that presenter is nil?
Hopefully some of you would help me out.
Thank you.
UITableView uses reusable cells, you can find a lot of info in google, here is one of the articles
https://medium.com/ios-seminar/why-we-use-dequeuereusablecellwithidentifier-ce7fd97cde8e
So creating an instance of UITableViewCell in AppDelegate you're doing nothing, it is immediately dispose
Create presenter
// somewhere in your MainTabViewController
let cellPresenter = ListCellPresenter(view: self)
To inject presenter you should use tableView(_:cellForRowAt:) method
// later in code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! ListCell
cell.inject(presenter: cellPresenter)
if let item = presenter.item(row: indexPath.row) {
cell.configure(item: item)
}
return cell
}

TextFields don't get updated with values in unwind function Swift

I have textfields that should get a value displayed once returning from a TableViewController where user selects a cell. I get that value in unwindfunction, but textfieldsdon't get updated. When printing the value it prints correctly on unwinding, so unwindshould be set correctly, but it just don't get displayed in it's textfield. I also tried prepare(for unwind:in TableViewControllerbut with same results. Can you see what I'm doing wrong?
As always many thanks.
unwind function:
#IBAction func unwindToDetailsVc(segue: UIStoryboardSegue) {
//Insert function to be run upon dismiss of VC2
print("unwindSegue triggered")
if let vc = segue.source as? CityTableViewController {
print("segue source is city vc : \(String(describing: vc.city!))")
self.cityTextField.text = vc.city
}
if let vc = segue.source as? RegionTableViewController {
print("segue source is region vc : \(String(describing: vc.region!))")
self.regionTextField.text = vc.region
}
if let vc = segue.source as? CountryTableViewController {
print("segue source is country vc : \(String(describing: vc.country!))")
self.countryTextField.text = vc.country
}
}
didSelect in TableView:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! CityTableViewCell
self.city = cell.cityLabel.text ?? ""
performSegue(withIdentifier: "unwindSegue", sender: self)
// self.dismiss(animated: true, completion: nil)
}
prepare for unwind:
override func prepare(for unwind: UIStoryboardSegue, sender: Any?) {
if unwind.identifier == "unwindSegue" {
if let detailsVc = unwind.destination as? ShopDetailsTableViewController {
detailsVc.cityTextField.text! = city
}
}
}
textfield delegate function:
func textFieldDidBeginEditing(_ textField: UITextField) {
print("Editing textfield")
if textField.accessibilityIdentifier == "city" {
print("Editing city textfield")
performSegue(withIdentifier: "citySegue", sender: self)
} else if textField.accessibilityIdentifier == "region" {
print("Editing regio textfield")
performSegue(withIdentifier: "regionSegue", sender: self)
} else if textField.accessibilityIdentifier == "country" {
print("Editing country textfield")
performSegue(withIdentifier: "countrySegue", sender: self)
}
}
You can simply use a closure to solve this kind of problem statement,
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBAction func openTableVC(_ sender: UIButton) {
if let controller = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TableViewController") as? TableViewController {
controller.handler = {[weak self](city) in
DispatchQueue.main.async {
self?.textField.text = city
}
}
self.navigationController?.pushViewController(controller, animated: true)
}
}
}
class TableViewController: UITableViewController {
var handler: ((String)->())?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let city = "Delhi"
self.handler?(city)
self.navigationController?.popViewController(animated: true)
}
}
The above code is generic and will work in every case from where you want to open TableViewController.
I finally found out what I was doing wrong, I was calling the functions loading from CoreDatauser details and displaying them in viewWillAppear. Once I moved them in saveButtonafter the saving to CoreDatafunction call, it all works as expected. Textfield get updated with select values from tableviews.
Many thank to #PGDev for sharing a more convenient way of coding this, without all the if elsestatements and unwinds. Great example of higher level coding.

Autolayout Contraints Changing When View Is Reloaded - Swift

recently in the search VC of my app I have been having a major problem with the contraints for weeks. I've redone them 4 times now with no improvement. My tableview cell is not complex -- an imageView located at the left and two labels on top of each other. Most cells size fine. The only thing is that(without pattern) two cells are randomly messed up(the labels and imageView move way out of place). You can see here:
Here is a picture of the constraints for the vc:
The strange thing is that this sometimes occurs when I toggle the scopeButtons up top. I looked in my code to see if I could fix this when the scopeButton reloads the tableview, but I could not find a problematic instance. My code is below:
import UIKit
import Firebase
import Kingfisher
class SearchPostsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var idArray:[String] = []
var detailViewController: DetailViewController? = nil
var candies = [Person]()
var filteredCandies = [Person]()
let searchController = UISearchController(searchResultsController: nil)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = false
//initiate the search bar that appears up top when view is segued to
}
if let selectionIndexPath = self.tableView.indexPathForSelectedRow {
self.tableView.deselectRow(at: selectionIndexPath, animated: animated)
}
self.tableView.reloadData()
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 11.0, *) {
navigationItem.hidesSearchBarWhenScrolling = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
Database.database().reference()
.child("\(UserData().mySchool!)/posts")
.queryOrderedByKey()//keys were out of order, so we have to use this to help
.observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot.childrenCount)
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.key)
self.idArray.append(child.key)
}
var reversedNames = [String]()
for arrayIndex in 0..<self.idArray.count {
reversedNames.append(self.idArray[(self.idArray.count - 1) - arrayIndex])
//reverse names so we dont have to sort the cells by date
}
for x in reversedNames{
self.searchNames(id: x)//get names from the ids here, tada!!!
self.tableView.reloadData()
}
})
//self.tableView.reloadData()//without this, the results wouldnt show up right away
searchController.searchBar.setScopeBarButtonTitleTextAttributes([NSAttributedStringKey.foregroundColor.rawValue: UIColor.white], for: .normal)
searchController.searchBar.scopeButtonTitles = ["Posts", "Users"]
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search posts or usernames"
searchController.searchBar.showsScopeBar = true
navigationItem.searchController = searchController
definesPresentationContext = true
if let splitViewController = splitViewController {
let controllers = splitViewController.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 74
}
// override func viewWillDisappear(_ animated: Bool) {
// candies.removeAll()
// }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DAY69:GRADUATION DAY", for: indexPath) as! SeachCell
cell.cellImageVIew.frame.size.width = 48
cell.cellImageVIew.frame.size.height = 48
let personUser: Person
if isFiltering() {
personUser = filteredCandies[indexPath.row]
} else {
personUser = candies[indexPath.row]
}
//PERSON USER IS IMPORTANT!!!!! ^^^
cell.nameLabel.text = personUser.name.removingPercentEncoding
cell.messageLabel.text = personUser.category.removingPercentEncoding
let name = personUser.name
Database.database().reference().child("users/\(name)/profileImageURL").observe(.value, with: { (snapshot) in
let profURL = "\(snapshot.value!)"
let profIRL = URL(string: profURL)
//set up imageview
cell.cellImageVIew.layer.borderWidth = 1
cell.cellImageVIew.layer.masksToBounds = false
cell.cellImageVIew.layer.borderColor = UIColor.black.cgColor
cell.cellImageVIew.layer.cornerRadius = cell.cellImageVIew.frame.height/2
cell.cellImageVIew.clipsToBounds = true
cell.cellImageVIew.contentMode = .scaleAspectFill
cell.cellImageVIew.kf.indicatorType = .activity
cell.cellImageVIew.kf.setImage(with: profIRL)
})
//TODO: make an extension of imageview to do all this for me. It's getting to be ridiculous
return cell
}
func searchNames(id: String){
// var message = String()
// var name = String()
Database.database().reference().child("\(UserData().mySchool!)/posts/\(id)/message").observe(.value, with: { (snapshot) in
// message = snapshot.value as! String
Database.database().reference().child("\(UserData().mySchool!)/posts").child("\(id)/username").observe(.value, with: { (username) in
// name = username.value as! String
let user = Person(category: "\(snapshot.value!)", name: "\(username.value!)", id: id)
self.candies.append(user)
print( "\(snapshot.value!)", "\(username.value!)")
self.tableView.reloadData()
})
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
return filteredCandies.count
}
return candies.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
tableView.estimatedRowHeight = 74.0
tableView.rowHeight = UITableViewAutomaticDimension
return UITableViewAutomaticDimension
}
// func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
// return 74
// }
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "DAY69:GRADUATION DAY", for: indexPath) as! SeachCell
cell.cellImageVIew.frame.size.width = 48
cell.cellImageVIew.frame.size.height = 48
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles?[searchBar.selectedScopeButtonIndex]
if scope == "Users"{
let username = candies[indexPath.row].name
print(username)
self.tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "userClicked", sender: username)
}
if scope == "Posts"{
let post = candies[indexPath.row].category
let user = candies[indexPath.row].name
let id = candies[indexPath.row].id
print(post)
let defaults = UserDefaults.standard
defaults.set(id, forKey: "ID")
let def2 = UserDefaults.standard
def2.set(post, forKey: "Post")
def2.set(user, forKey: "USER")
self.tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "postCellTapped", sender: nil)
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//__________tbv methods above________________________________________________
func searchBarIsEmpty() -> Bool {
// Returns true if the text is empty or nil
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredCandies = candies.filter({(candy : Person) -> Bool in
let doesCategoryMatch = (scope == "Posts") || (scope == "Users")
print(searchText)
if searchBarIsEmpty() {
return doesCategoryMatch
}
if scope == "Users"{
return doesCategoryMatch && candy.name.lowercased().contains(searchText.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!.lowercased())
}
else{
return doesCategoryMatch && candy.category.lowercased().contains(searchText.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!.lowercased())
}
})
tableView.reloadData()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let personalUser: Person
if isFiltering() {
personalUser = filteredCandies[indexPath.row]
} else {
personalUser = candies[indexPath.row]
}
}
}
if segue.identifier == "userClicked" {
if let nextView = segue.destination as? UserProfileController {
nextView.selectedUser = "\(sender!)"
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func isFiltering() -> Bool {
let searchBarScopeIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScopeIsFiltering)
}
}
extension SearchPostsController: UISearchResultsUpdating {
// MARK: - UISearchResultsUpdating Delegate
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
extension SearchPostsController: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
Any help would be greatly appreciated in trying to fix the constraints! Thanks!