Is there a way I can identify .storyboard associated with viewController? - swift

In a storyboard it is possible to find the id of the associated viewController. But from what I can tell, it's not possible the other way around: that is, in a viewController, can I see what storyboard it is connected to?
Thing is, I'm currently working with a viewController but have no clue what storyboard it is associated with.
Here is code with suggestion from Shreeram-Bhat:
import UIKit
protocol PickNumberVieDelegate: class {
func dismissPickNumberView(tempDef:String, tempAlter:String)
}
class PickNumberViewController: UIViewController,UITableViewDataSource, UITableViewDelegate{
weak var delegate: PickNumberVieDelegate?
var whatTypOfNumber = 0
#IBOutlet weak var tableview: UITableView!
#IBOutlet weak var indicator: UIActivityIndicatorView!
var arr = [PresentationNumberOption]()
var tempAlernatNr = ""
var tempDefaultNr = ""
override func viewDidLoad() {
super.viewDidLoad()
//HERE
var storyboard: UIStoryboard? { get }
print("🐥here", storyboard);
self.setUserInterfaceStyleLight()
if (whatTypOfNumber == 0) {
self.title = NSLocalizedString("TimeControlled AfterWork Time" , comment: "")
}else if (whatTypOfNumber == 1){
self.title = NSLocalizedString("TimeControlled Showing Number" , comment: "")
}else{
self.title = NSLocalizedString("Display Number", comment: "")
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
var mobilNumber = ""
if let mobileNumberForThisDevice = CurrentPerson.shared().thisMobilePhone(){
mobilNumber = mobileNumberForThisDevice.address
};
Communication.sendGetCustomPresentationNumberOptionsforNumber(mobilNumber, withCallbackMethod: #selector(self.gotPresentationNumberOptions(_:)), callbackFailMethod: #selector(self.failedToGetNumber), on: self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let CellIdentifier = "CellId"
var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: CellIdentifier)
}
var option: PresentationNumberOption?
option = arr[indexPath.row]
var str = option?.displayText()
str = str?.replacingOccurrences(of: "[", with: "")
str = str?.replacingOccurrences(of: "]", with: "")
cell?.textLabel?.text = str
if( whatTypOfNumber == 0){
if (ModelManager.shared()?.tcsm.defaultNr == option?.address) {
cell?.accessoryType = .checkmark
} else {
cell?.accessoryType = .none
}
}else{
if (ModelManager.shared()?.tcsm.alternateNr == option?.address) {
cell?.accessoryType = .checkmark
} else {
cell?.accessoryType = .none
}
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tempAlernatNr = (ModelManager.shared()?.tcsm.alternateNr)!
tempDefaultNr = (ModelManager.shared()?.tcsm.defaultNr)!
let presentationNumber: PresentationNumberOption?
presentationNumber = arr[indexPath.row]
if( whatTypOfNumber == 0){
if(presentationNumber?.address == ""){
if presentationNumber?.type == kPresentationNumberOptionTypeMobileNumber{
self.showVerificationScreen()
}else{
ModelManager.shared()?.tcsm.defaultNr = presentationNumber?.address ?? ""
delegate?.dismissPickNumberView(tempDef: tempDefaultNr, tempAlter: tempAlernatNr)
self.navigationController?.popViewController(animated: true)
}
}else{
ModelManager.shared()?.tcsm.defaultNr = presentationNumber?.address ?? ""
delegate?.dismissPickNumberView(tempDef: tempDefaultNr, tempAlter: tempAlernatNr)
self.navigationController?.popViewController(animated: true)
}
}else{
if(presentationNumber?.address == ""){
if presentationNumber?.type == kPresentationNumberOptionTypeMobileNumber{
self.showVerificationScreen()
}else{
ModelManager.shared()?.tcsm.alternateNr = presentationNumber?.address ?? ""
delegate?.dismissPickNumberView(tempDef: tempDefaultNr, tempAlter: tempAlernatNr)
self.navigationController?.popViewController(animated: true)
}
}else{
ModelManager.shared()?.tcsm.alternateNr = presentationNumber?.address ?? ""
delegate?.dismissPickNumberView(tempDef: tempDefaultNr, tempAlter: tempAlernatNr)
self.navigationController?.popViewController(animated: true)
}
}
}
#objc func gotPresentationNumberOptions(_ presentationNumbers: [Any]?) {
if let aNumbers = presentationNumbers {
self.arr = aNumbers as! [PresentationNumberOption]
}
indicator.stopAnimating()
tableview.reloadData()
}
#objc func failedToGetNumber() {
//ALog("Failed to get presentation number options:%#", error)
indicator.stopAnimating()
}
func numberOfSections(in tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
func showVerificationScreen() {
print("🐥Show Verification Screen");
let nextController = NumberVerificationViewController(nibName: "NumberVerificationViewController", bundle: Bundle.main)
navigationController?.pushViewController(nextController, animated: true)
}
}

You can get it from "storyboard" property. Below is the declaration of the property.
var storyboard: UIStoryboard? { get }
It will be nil if you have instantiated ViewController from code. You get it like,
if let storyboard = self.storyboard {
if let name = storyboard.value(forKey: "name") as? String {
print(name)
}
}

Related

How to deselect all buttons when one of them is selected

I have a tableview and inside one of my rows I have buttons. I want to deselect all buttons when one is selected and change the background color to the red when button is selected. I saw lots of example but I couldn't do it in my own code.
func configure(_ modelArray : [UnitAndColors], colorTitle:String, packingTitle:String) {
self.unitStackView.removeAllArrangedSubviews()
self.arrayUnitsAndColor?.removeAll()
self.arrayUnitsAndColor = modelArray
let filteredArray = self.arrayUnitsAndColor?.unique { $0.colorDesc }
var i : Int = 0
filteredArray?.forEach { units in
let vw = self.createUnits(units, tag: i)
self.unitStackView.addArrangedSubview(vw)
vw.snp.makeConstraints {
$0.size.equalTo(40.0)
}
i = i + 1
}
}
func createUnits(_ model : UnitAndColors, tag: Int) -> UIStackView {
let stackViewMain = UIStackView()
stackViewMain.axis = .horizontal
stackViewMain.spacing = 4
let labelPackingUnit = UIButton()
labelPackingUnit.backgroundColor = Colors().colorWhite
labelPackingUnit.tag = tag
labelPackingUnit.setTitleColor(Colors().colorRed, for: .normal)
labelPackingUnit.addTarget(self, action: #selector(selectUnit(_:)), for: .touchUpInside)
labelPackingUnit.titleLabel?.font = UIFont.fontBold16
labelPackingUnit.contentHorizontalAlignment = .center
labelPackingUnit.setBorder(width: 1, color: Colors().colorRed)
labelPackingUnit.setCornerRound(value: 20.0)
labelPackingUnit.setTitle(model.unitDesc, for: .normal)
stackViewMain.addArrangedSubview(labelPackingUnit)
labelPackingUnit.snp.makeConstraints {
$0.size.equalTo(40)
}
return stackViewMain
}
#objc private func selectButton(_ sender : UIButton) {
let tag : Int = sender.tag
guard let model : UnitAndColors = self.arrayUnitsAndColor?.filter({ $0.colorDesc == selectedColorName })[tag] else { return }
selectedUnit = model.unitDesc ?? ""
delegate?.changePrice(selectedPrice: model.modelPrice, arrayUnitsAndColor ?? [])
}
It's pretty frustrating to understand what's happening in your code, but I think that's what you're looking for:
#objc private func selectButton(_ sender : UIButton) {
let tag : Int = sender.tag
guard let model: UnitAndColors = self.arrayUnitsAndColor?.filter({ $0.colorDesc == selectedColorName })[tag] else { return }
selectedUnit = model.unitDesc ?? ""
for stackView in unitStackView.arrangedSubviews as? [UIStackView] ?? [] {
guard let button = stackView.arrangedSubviews.first as? UIButton else {
return
}
button.isSelected = false
button.backgroundColor = .clear
}
selectedUnit.isSelected = true
selectedUnit.backgroundColor = .red
delegate?.changePrice(selectedPrice: model.modelPrice, arrayUnitsAndColor ?? [])
}
var selectedRows: [Int] = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowMultipleSelection = true
}
func selectAll(){
for i in 0..<totalRows.count{
selectedRows.append(i)
let indexPath = IndexPath(row: i, section: 0)
self.tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
}
func deselectAll(){
for i in 0..<totalRows.count{
selectedRows.append(i)
let indexPath = IndexPath(row: i, section: 0)
self.tableView.deselectRow(at: indexPath, animated: true)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
selectedRows.append(indexPath.row)
print(selectedRows)
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let index = selectedRows.firstIndex(of: indexPath.row) {
selectedRows.remove(at: index)
}
print(selectedRows)
if selectedRows.isEmpty{
}
}
}
class TableViewCell: UITableViewCell{
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if (selected) {
print("selected")
contentView.backgroundColor = .red
} else {
contentView.backgroundColor = .red
}
}
}

Dictionary search the key and get the value

I got a plist object which contains all the words key=english and value=malay and I assigned in to 2 different arrays which is english and malay. Now I want a textfield where I want to search the english word and print the malay word in the label.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
#IBOutlet weak var selectedLabel: UILabel!
#IBOutlet weak var searchText: UITextField!
#IBOutlet weak var wordTable: UITableView!
var english = [String]()
var malay = [String]()
var words: [String: String] = [:]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
wordTable.dataSource = self
wordTable.delegate = self
searchText.delegate = self
if let path = Bundle.main.path(forResource: "words", ofType: "plist"){
if let plistData = FileManager.default.contents(atPath: path){
do {
let plistObject = try PropertyListSerialization.propertyList(from: plistData, options: PropertyListSerialization.ReadOptions(), format: nil)
words = (plistObject as? [String: String])!
english = [String] (words.keys)
malay = [String] (words.values)
} catch {
print("Error Serialize")
}
} else {
print("Error reading data")
}
} else {
print("Property list")
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return english.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: UITableViewCell!
cell = tableView.dequeueReusableCell(withIdentifier: "tabelCell")
if cell == nil {
cell = UITableViewCell(
style: UITableViewCellStyle.value2,
reuseIdentifier: "tableCell")
print("creating a table cell")
}
cell!.textLabel!.text = english[indexPath.row]
cell!.detailTextLabel?.text = malay[indexPath.row]
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedLabel.text = malay[indexPath.row]
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// Hide the keyboard
textField.resignFirstResponder()
return true
}
#IBAction func searchBtn(_ sender: UIButton) {
let result = words.filter {$0.key == searchText.text}
if result.count > 0 {
print(result)
selectedLabel.text! = result.values //error
} else {
print("Not found")
}
}
}
the output I expecting is textfield(Bus) which is english word then in the label show me the malay word(Bas)
You have a plist file as a Dictionary. So you can get the dictionary object from the plist file and already answer here.
Make a structure for better data binding.
struct Word {
var english: String
var malay: String
}
Then declare an array of words globally in your ViewController.
var words: [Word] = [] // An empty array
In viewDidLoad: fetch data from plist file.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
wordTable.dataSource = self
wordTable.delegate = self
searchText.delegate = self
if let path = Bundle.main.path(forResource: "words", ofType: "plist") {
if let plistData = FileManager.default.contents(atPath: path){
do {
guard let plistObject = try PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String: String] else {
// Plist is not [String: String]
return
}
// Here you need to change the code. Converting the dictionary into struct array
var words: [Word] = plistObject.map {Word(english: $0.key, malay: $0.value)}
/// Then sort by english word if needed
words.sorted {$0.english < $1.english}
} catch {
print("Error Serialize")
}
} else {
print("Error reading data")
}
} else {
print("Property list")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return words.count
}
Update your cell data as well.
cell!.textLabel!.text = words[indexPath.row].english
cell!.detailTextLabel?.text = words[indexPath.row].malay
And your button action with minor modification:
#IBAction func searchBtn(_ sender: UIButton) {
let result = words.filter {$0.english == searchedText}
if let word = result.first {
selectedLabel.text = word.malay
} else {
selectedLabel.text = "" // No data found
}
}
You can replace $0.english == searchedText with {$0.english.contains(searchedText)} if you want to filter with contains, But in that case you might get the multiple result. I assume that in your case you need it as a translator so use ==.
Why don't you search in your plist object? I think it is simpler
#IBAction func searchBtn(_ sender: UIButton) {
guard let words = plistObject as? [String: String], let key = searchText.text else { return }
selectedLabel.text = words[key] ?? ""
}
Something like this.

want to display data on Home screen from searched data coming from api

I am making an app in which i have a home screen which lists categories and store data into tableview. in tableview i have a search button which navigates me to search screen when clicked and on search screen i have taken custom textfield to search. Now i want that when i search something and it shows in result then when i click on searched result then it should show data of the category or store selected on previous home screen. And the search result is category data and store data. All data is coming from api and am using tableview to show search data
screenshot for coupons api in which search data will be display:
screenshot for search api:
code for my search screen is as below:
class SearchPageController: UIViewController {
#IBOutlet weak var searchTxtBar: UITextField!
#IBOutlet weak var searchTblView: UITableView!
var searchData = [ModelSearched]()
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
// Do any additional setup after loading the view.
searchTxtBar.delegate = self
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
//MARK: IBActions
#IBAction func toHomeScreen(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
func getSearchList(){
let params: Parameters=[
"search":searchTxtBar.text!,
]
if ApiUtillity.sharedInstance.isReachable()
{
ApiUtillity.sharedInstance.StartProgress(view: self.view)
APIClient<ModelBaseSearchList>().API_GET(Url: SD_GET_SearchList, Params: params as [String:AnyObject], Authentication: true, Progress: true, Alert: true, Offline: false, SuperVC: self, completionSuccess: { (modelResponse) in
ApiUtillity.sharedInstance.StopProgress(view: self.view)
if(modelResponse.success == true) {
self.searchData.removeAll()
let resul_array_tmp_new = modelResponse.searched! as NSArray
if resul_array_tmp_new.count > 0 {
for i in modelResponse.searched! {
self.searchData.append(i)
}
}
}
else {
self.view.makeToast(modelResponse.message)
}
ApiUtillity.sharedInstance.StopProgress(view: self.view)
self.searchTblView.reloadData()
}) { (failed) in
ApiUtillity.sharedInstance.StopProgress(view: self.view)
self.view.makeToast(failed.localizedDescription)
}
}
else
{
self.view.makeToast("No Internet Connection..")
}
}
#IBAction func clearSearchData(_ sender: UIButton) {
self.searchData.removeAll()
self.searchTxtBar.text = ""
searchTblView.reloadData()
}
}
//MARK: Tableview delegates and datasource methods
extension SearchPageController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "catstoredata")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "catstoredata")
}
let dict = searchData[indexPath.row]
cell?.selectionStyle = .none
cell?.textLabel?.text = dict.name
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
self.navigationController?.popViewController(animated: true)
}
}
//MARK: textfield delegates method
extension SearchPageController: UITextFieldDelegate{
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.searchTxtBar.resignFirstResponder()
self.searchTblView.reloadData()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.getSearchList()
}
}
code for my home screen is as below:
class HomeViewController: UIViewController {
var couponsData = [ModelCoupons]()
var colorList = [UIColor]()
var selectedIds = [Int]()
#IBOutlet var logoutPopup: UIView!
#IBOutlet weak var homeTblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//append colors
colorList.append(UIColor(rgb: 0xdd191d))
colorList.append(UIColor(rgb: 0xd81b60))
colorList.append(UIColor(rgb: 0x8e24aa))
colorList.append(UIColor(rgb: 0x5e35b1))
colorList.append(UIColor(rgb: 0x3949ab))
colorList.append(UIColor(rgb: 0x4e6cef))
colorList.append(UIColor(rgb: 0x00acc1))
colorList.append(UIColor(rgb: 0x00897b))
colorList.append(UIColor(rgb: 0x0a8f08))
colorList.append(UIColor(rgb: 0x7cb342))
colorList.append(UIColor(rgb: 0xc0ca33))
colorList.append(UIColor(rgb: 0xfdd835))
colorList.append(UIColor(rgb: 0xfb8c00))
colorList.append(UIColor(rgb: 0xf4511e))
colorList.append(UIColor(rgb: 0xf4511e))
colorList.append(UIColor(rgb: 0x757575))
colorList.append(UIColor(rgb: 0x546e7a))
self.homeTblView.register(UINib(nibName: "HomeCell", bundle: nil), forCellReuseIdentifier: "HomeCell")
self.homeTblView.register(UINib(nibName: "Home1Cell", bundle: nil), forCellReuseIdentifier: "Home1Cell")
self.post_CouponsData()
self.homeTblView.reloadData()
print(selectedIds)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
func changeDateForamte(dateString: String, currentDateFormate: String, newDateFormate: String) -> String
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = currentDateFormate
let newDate = dateFormatter.date(from: dateString)
dateFormatter.dateFormat = newDateFormate
return dateFormatter.string(from: newDate!)
}
#IBAction func logout_yes(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(vc, animated: true)
}
#IBAction func logout_no(_ sender: Any) {
self.logoutPopup.removeFromSuperview()
}
#objc func openDeals(sender: UIButton) {
let svc = SFSafariViewController(url: URL(string: couponsData[sender.tag].guid!)!)
self.present(svc, animated: true, completion: nil)
}
//MARK: IBActions
#IBAction func toCategoryScreen(_ sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}
#IBAction func toSearchPage(_ sender: UIButtonX) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SearchPageController") as! SearchPageController
self.navigationController?.pushViewController(vc, animated: true)
}
#IBAction func logoutBtnPressed(_ sender: Any) {
ApiUtillity.sharedInstance.AddSubViewtoParentView(parentview: self.view, subview: logoutPopup)
}
func post_CouponsData() {
if ApiUtillity.sharedInstance.isReachable() {
var params = [String : String]()
params ["term_ids"] = "\(self.selectedIds.map(String.init).joined(separator: ","))"
ApiUtillity.sharedInstance.StartProgress(view: self.view)
APIClient<ModelBaseCouponsList>().API_POST(Url: SD_POST_CouponsList, Params: params as [String:AnyObject], Authentication: true, Progress: true, Alert: true, Offline: false, SuperVC: self, completionSuccess: { (modelResponse) in
ApiUtillity.sharedInstance.StopProgress(view: self.view)
if(modelResponse.success == true) {
ApiUtillity.sharedInstance.StopProgress(view: self.view)
let dict = modelResponse.coupons
for i in dict! {
self.couponsData.append(i)
}
}else {
self.view.makeToast(modelResponse.message)
}
ApiUtillity.sharedInstance.StopProgress(view: self.view)
self.homeTblView.reloadData()
}) { (failed) in
self.view.makeToast(failed.localizedDescription)
ApiUtillity.sharedInstance.StopProgress(view: self.view)
}
}else {
self.view.makeToast("No internet connection...")
ApiUtillity.sharedInstance.StopProgress(view: self.view)
}
}
}
extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return couponsData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if !couponsData[indexPath.row].postImage!.isEmpty {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeCell", for: indexPath) as! HomeCell
let dict = couponsData[indexPath.row]
if let postTitle = dict.postTitle, postTitle.count != 0 {
cell.ticket_postTitle.text = postTitle
}
if let postContent = dict.postContent, postContent.count != 0 {
cell.ticket_postContent.text = postContent
}
if let storeName = dict.stores, storeName.count != 0 {
cell.storename.text = storeName
}
cell.home_image.image = UIImage(named: dict.postImage!)
cell.ticketImageView.tintColor = colorList[indexPath.row]
cell.ticket_ValidDate.text = self.changeDateForamte(dateString: dict.validTill!, currentDateFormate: "yyyy-MM-dd", newDateFormate: "dd MMMM yyyy")
// cell.ticketImageView.tintColor = .random()
cell.goToDealBtn.tag = indexPath.row
cell.goToDealBtn.addTarget(self, action: #selector(self.openDeals), for: .touchUpInside)
return cell
}else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Home1Cell", for: indexPath) as! Home1Cell
let dict = couponsData[indexPath.row]
if let postTitle = dict.postTitle, postTitle.count != 0 {
cell.ticket_postTitle.text = postTitle
}
if let postContent = dict.postContent, postContent.count != 0 {
cell.ticket_postContent.text = postContent
}
if let storeName = dict.stores, storeName.count != 0 {
cell.storename.text = storeName
}
cell.ticketImageView.tintColor = colorList[indexPath.row]
cell.ticket_ValidDate.text = self.changeDateForamte(dateString: dict.validTill!, currentDateFormate: "yyyy-MM-dd", newDateFormate: "dd MMMM yyyy")
// cell.ticketImageView.tintColor = .random()
cell.goToDealBtn.tag = indexPath.row
cell.goToDealBtn.addTarget(self, action: #selector(self.openDeals), for: .touchUpInside)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if !couponsData[indexPath.row].postImage!.isEmpty {
return 339.0
}else {
return 234.0
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if !couponsData[indexPath.row].postImage!.isEmpty {
return 339.0
}else {
return 234.0
}
}
}

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!

Swift's deinit is not called

private let DBItemCellIdentifier = "ItemCellIdentifier"
private let DBItemSegueIdentifier = "ItemSegueIdentifier"
class DBItemsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, DBItemTableViewCellDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var previousButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var categoryNameLabel: UILabel!
private var elements = [Any]()
private var currentItemIndex = 0
private var isFetching = false
private weak var currentCategory: DBCategory? {
didSet {
updateView()
}
}
var categories = [DBCategory]()
var currentCategoryIndex = 0
//MARK: - Class Methods
//MARK: - Initialization
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100.0
tableView.tableFooterView = UIView(frame: CGRectZero)
setupUserAndCartButtons()
fetchItems()
}
deinit {
print("deinit")
}
//MARK: - Actions
#IBAction func nextButtonTapped(sender: UIButton) {
currentCategoryIndex = min(currentCategoryIndex + 1, categories.count - 1)
fetchItems()
}
#IBAction func previousButtonTapped(sender: UIButton) {
currentCategoryIndex = max(currentCategoryIndex - 1, 0)
fetchItems()
}
//MARK: - Private
private func fetchItems() {
tableView.alpha = 0
currentCategory = nil
if !categories.isEmpty && !isFetching {
let category = categories[currentCategoryIndex]
currentCategory = DBCategory.findCategoryWithIdentifier(category.identifier)
if currentCategory == nil {
SVProgressHUD.show()
}
isFetching = true
DBNetworkClient.sharedClient().itemsForCategory(category, completionBlock: { error in
defer {
self.isFetching = false
SVProgressHUD.dismiss()
UIAlertController.showAlertFromError(error)
}
self.currentCategory = DBCategory.findCategoryWithIdentifier(category.identifier)
})
}
}
private func updateView() {
let category = categories[currentCategoryIndex]
title = category.menu.location.name
categoryNameLabel.text = category.name
previousButton.hidden = currentCategoryIndex == 0 ? true : false
nextButton.hidden = currentCategoryIndex == categories.count - 1 ? true : false
prepareElements()
tableView.reloadData()
UIView.animateWithDuration(0.5, animations: {
self.tableView.alpha = 1
})
}
private func prepareElements() {
elements.removeAll(keepCapacity: false)
if let items = currentCategory?.items {
for item in items {
elements.append(item)
}
}
if let sets = currentCategory?.sets {
for set in sets {
elements.append(set)
}
}
elements.sortInPlace {
let left = ($0 as? DBSet)?.position ?? ($0 as? DBItem)?.position
let right = ($1 as? DBSet)?.position ?? ($1 as? DBItem)?.position
return left < right
}
}
//MARK: - Overridden
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let element = elements[currentItemIndex]
if segue.identifier == DBItemSegueIdentifier {
let itemViewController = segue.destinationViewController as! DBItemViewController
itemViewController.prepareWithElement(element)
}
}
//MARK: - UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0 //when I change to elements.count, deinit is not called
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(DBItemCellIdentifier, forIndexPath: indexPath) as! DBItemTableViewCell
let element = elements[indexPath.row]
if let item = element as? DBItem {
cell.configureCellWithItem(item)
} else if let set = element as? DBSet {
cell.configureCellWithSet(set)
}
cell.delegate = self
return cell
}
//MARK: - UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
currentItemIndex = indexPath.row
performSegueWithIdentifier(DBItemSegueIdentifier, sender: tableView.cellForRowAtIndexPath(indexPath))
}
//MARK: - DBItemTableViewCellDelegate
func itemTableViewCell(cell: DBItemTableViewCell, willPresentSetGroupsViewControllerForSet set: DBSet) {
presentSetOrderControllerWithOrder(DBSetOrder(set: set))
}
func itemTableViewCell(cell: DBItemTableViewCell, willPresentItemMealSizesViewControllerForItem item: DBItem) {
presentItemOrderControllerWithOrder(DBItemOrder(item: item))
}
}
Why my deinit is not called. I will offer 100 bounty once I will be able to do this, and award to that one, who help me solve this problem... I will offer a bounty even after solving the problem.
VERY IMPORTANT INFO:
this code calls deinit. IT IS WORKING. Because number of rows is 0. But I need to have there elements.count. When I change to this, deinit is not called.
EDIT:
func itemsForCategory(category: DBCategory, completionBlock: DBErrorHandler) {
let query = "locations/" + category.menu.location.identifier + "/categories/" + category.identifier
GET(query, parameters: nil, success: { operation, response in
if let error = NSError(response: response) {
completionBlock(error)
} else {
self.coreDataAssistant.parseAndSaveItemsToPersistentStore(response as? NSDictionary, completionBlock: { error in
completionBlock(error)
})
}
}) { operation, error in
let responseError = NSError(response: operation.responseObject)
completionBlock(responseError ?? error)
}
}
You are assigning self as your table view cell's delegate:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(DBItemCellIdentifier, forIndexPath: indexPath) as! DBItemTableViewCell
let element = elements[indexPath.row]
if let item = element as? DBItem {
cell.configureCellWithItem(item)
} else if let set = element as? DBSet {
cell.configureCellWithSet(set)
}
// HERE
cell.delegate = self
return cell
}
The cell's delegate property is defined as follows:
var delegate: DBItemTableViewCellDelegate?
This creates a strong reference between the cell and the delegate (your view controller). The cell is also retained by the table view. This creates a retain cycle.
You will need to change the definition of the delegate property to be weak:
weak var delegate: DBItemTableViewCellDelegate?
Edit based on comment:
Your DBItemTableViewCellDelegate definition will need to be defined as a class-only protocol
protocol DBItemTableViewCellDelegate: class {
...
}