PickerView inside tableView cell - ios

I have created a tableview which takes date difference and create that number of tableview cell, each table view cell consist of textview and and picker view against textview to pick value. The problem is when i pick value for the 1 position using PickerView then the selected value is also reflected on the other cell
code for the contorller
here hfDetailsArr contains total number of days difference
--function for getting date difference count {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
let currentCalendar = NSCalendar.current
dateFormatter.date(from: dateFormatter.string(from:date1 as Date))
let temp = currentCalendar.dateComponents([.day], from: dateFormatter.date(from: dateFormatter.string(from:date1 as Date))!, to: dateFormatter.date(from: dateFormatter.string(from:date2 as Date))!)
hfDetailsArr.removeAllObjects();
//print(temp.day!)
if !(temp.day! < 0) && (temp.day! != 0) {
for i in 0 ..< (temp.day! + 1){
let HFDetailsDates = currentCalendar.date(byAdding: .day, value: i, to: date1 as Date)
DataDetails = dateFormatter.string(from: HFDetailsDates!)
hfDetailsArr.add(DataDetails)
ApplyForDate.append( dateFormatter.string(from: HFDetailsDates!) + ",")
}
//print(ApplyForDate)
}
HFDEtailsTable.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if hfDetailsArr.count < 0 && hfDetailsArr.count != 0 {
return 0
}else{
return hfDetailsArr.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : HFDetailsCell! = tableView.dequeueReusableCell(withIdentifier: "cell") as! HFDetailsCell
cell.txtHFDateFeild.text = hfDetailsArr[indexPath.row] as? String
cell.cellDelegate = self
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("indeddsdsd \(indexPath.row)")
}
code for UITableview cell with pickerview inside it
protocol HFDetailsCellDelegate : class {
func ValueSelectedChage(sender:HFDetailsCell ,Id:String ,selectedCell:String)
}
class HFDetailsCell: UITableViewCell,UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource{
#IBOutlet var txtHFPicker: UITextField!
#IBOutlet var txtHFDateFeild: UITextField!
#IBOutlet var pickerView: UIPickerView! = UIPickerView()
var HfDtailsOneArr = ["Select","Full Day Leave","Half Day Leave"]
var position = 0
//var intHFStatusDetail = ""
/// internal static var HFtableView = UITableView()
weak var cellDelegate: HFDetailsCellDelegate?
//var LeaveDataIDArr = [String?]()
override func awakeFromNib() {
txtHFDateFeild.delegate = self
pickerView.delegate = self
txtHFPicker.inputView = pickerView
pickerView.backgroundColor = UIColor.white
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func txtleaveType(_ sender: UITextField) {
}
//print("buttonclick")
// //UIpickerView
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
//Set number of rows in components
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.HfDtailsOneArr.count
}
//Set title for each row
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return HfDtailsOneArr[row]
}
// Update textfield text when row is selected
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// self.cellDelegate?.ValueSelectedChage(sender:self, Id: HFDetailsLeaveCount(position: row), selectedCell: "")
txtHFPicker.text = HfDtailsOneArr[row]
print("pickerText:\(txtHFPicker.text!)")
}
func HFDetailsLeaveCount(position:Int)-> String {
switch position {
case 0:
return "0"
case 1:
return "8"
case 2:
return "4"
default:
return "nil"
}
}
}
in above image if i select value for date 04/08 then the same value is reflected for 12/08
-- any kind of help will be appreciated. Thank you

in table cell, on picker selection call ValueSelectedChage delegate method
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.cellDelegate?.ValueSelectedChage(sender:self, Id: HFDetailsLeaveCount(position: row), selectedCell: HfDtailsOneArr[row])
txtHFPicker.text = HfDtailsOneArr[row]
print("pickerText:\(txtHFPicker.text!)")
}
create a variable in viewcontroller to keep selected value
var hfSelectedDetails: [String: String?]?
in viewcontroller implement the delegate method
func ValueSelectedChage(sender:HFDetailsCell ,Id:String ,selectedCell:String) {
let indexpath = self.tableView.indexPathForRowAtPoint(cell.center)
let key = "\(indexpath.row)"
hfSelectedDetails[key] = selectedCell
....//other code if any
}
and in cellForRow add
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : HFDetailsCell! = tableView.dequeueReusableCell(withIdentifier: "cell") as! HFDetailsCell
cell.txtHFDateFeild.text = hfDetailsArr[indexPath.row] as? String
cell.cellDelegate = self
let key = "\(indexPath.row)"
if let selectedValue = hfSelectedDetails[key] {
cell.txtHFPicker.text = selectedValue
} else {
cell.txtHFPicker.text = nil
}
return cell
}

Related

How to compare values of two different UIPickerView?

I have custom UITableViewController with two custom UITableViewCell
Each of them contains UIPickerView
I want to compare values of these UIPickerViews
(I make the Maps App, and i wanna make the route, so i need do compare two points, and if they are different then "Make The Route" button will be set to Enabled State)
How can i do this? I tried to use didSet values, but it didn't work
Here are my classes:
class PlaceForRouteCell: UITableViewCell, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
// Выбираем все места в базе данных
let places = realm.objects(Place.self)
var selectedPlace = Place()
func getSelectedPlace() -> Place {
return selectedPlace
}
//число "барабанов"
func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 }
//число элементов в "барабане"
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
//если БД пуста, то предупредить об этом
return places.count != 0 ? places.count : 1
}
//содержимое "барабанов"
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if (places.count != 0) {
if (places.count == 1) { return "Добавьте еще одно место" }
return places[row].userName
}
return "Сохраненных мест нет"
}
//обрабатываем выбранный элемент
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if (places.count != 0) {
print(picker.tag)
selectedPlace = places[row]
// print("выделена строка №\(row), элемент \(places[row])'")
}
}
func setup() { picker.delegate = self }
}
And
class TableForRouteViewController: UIViewController, UITableViewDataSource, UIPickerViewDelegate{
// Выбираем все места в базе данных
let places = realm.objects(Place.self)
#IBOutlet weak var makeTheRouteButton: UIBarButtonItem!
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0,
width: tableView.frame.size.width, height: 1))
tableView.dataSource = self
let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
cell.picker.selectRow(1, inComponent: 0, animated: true)
var cellA = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! PlaceForRouteCell {
didSet {
print("первая ячейка изменилось")
}
}
var cellB = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
// print("tag of A: \(cellA.picker)")
// print("tag of B: \(cellB.picker.tag)")
var valueOfA = cellA.getSelectedPlace() {
didSet {
print("значение в первой ячейке изменилось")
}
}
print(valueOfA.userName)
var valueOfB = cellB.getSelectedPlace() {
didSet {
print("значение в первой ячейке изменилось")
}
}
print(valueOfB.userName)
if (places.count < 2 ) { makeTheRouteButton.isEnabled = false }
}
#IBAction func makeTheRouteButtonPressed(_ sender: UIBarButtonItem) {
let cellA = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! PlaceForRouteCell
let pointA = cellA.selectedPlace
let cellB = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
let pointB = cellB.selectedPlace
print("point A: \(pointA.userName), point B: \(pointB.userName)")
}
// var arePlacesEqual : Bool = comparing() {
// didSet {
// print("значения поменялись")
// }
// }
func comparing() -> Bool {
let cellA = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! PlaceForRouteCell
let cellB = tableView.cellForRow(at: IndexPath(row: 0, section: 1)) as! PlaceForRouteCell
let valueOfA = cellA.selectedPlace.userName
let valueOfB = cellB.selectedPlace.userName
if (valueOfA == valueOfB) { return true }
return false
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "placeForRouteCell", for: indexPath) as! PlaceForRouteCell
cell.picker.tag = indexPath[0]
cell.setup()
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch(section) {
case 0:return "Точка А"
case 1:return "Точка Б"
default :return ""
}
}
// количество секциий
func numberOfSections(in tableView: UITableView) -> Int { return 2 }
}
I created tableView with pickerView in cell.
In didSelectRow method i added NotificationCenter sender
My tableViewCell class:
let picker_array = ["one", "two", "three"]
class TableViewCell: UITableViewCell, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var picker: UIPickerView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.picker.delegate = self
self.picker.dataSource = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return picker_array.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "\(picker_array[row])"
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
NotificationCenter.default.post(name: Notification.Name("Notification"), object: nil)
}
}
In viewDidLoad i added NotificationCenter listener.
My ViewController class:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let dummy_data = [1,2]
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(methodOfReceivedNotification(notification:)), name: Notification.Name("Notification"), object: nil)
}
#objc func methodOfReceivedNotification(notification: Notification) {
for index in dummy_data {
let indexPath = IndexPath(row: index - 1, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
let selectRow = picker_array[cell.picker.selectedRow(inComponent: 0)]
print(selectRow)
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dummy_data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
return cell
}
}
When i change pickerView, console print current value from cells
I think it will help

PickerView not getting the updated value from Array

First of all please correct my topic subject or suggest an edit for the taggin in case if I mentioned somthing wrong.
I'm working on a store App project and I stuck in the selecting subSection VC because of the UIPickerView not getting the updated value from the array of target!
Here below is my code
class SelectSectionVC : UIViewController, UITextFieldDelegate {
var delegate : SelectSectionDelegate!
var staticVariable = SelectionCell.shared
override func viewDidLoad() {
super.viewDidLoad()
getSection()
setupCell()
self.pickertextField.alpha = 0
self.DoneBtn.isHidden = true
self.pickerView.isHidden = true
pickertextField.addTarget(self, action: #selector(textField_CatchedData(_:)), for: .editingDidEnd )
}
#IBOutlet weak var CollectionView: UICollectionView!
#IBOutlet weak var pickerView: UIPickerView!{
didSet{ pickerView.delegate = self ; pickerView.dataSource = self }}
var selectedSection : SectionsObject?
var sectionsArray : [SectionsObject] = []
func getSection(){
SectionsAPI.getAllsections { (section) in
self.sectionsArray.append(section)
DispatchQueue.main.async {
self.CollectionView.reloadData()
}
}
}
// Products type Arrays
var type_FP : [String] = ["FP1", "FP2"]
var type_TP : [String] = ["TP1", "TP2"]
var type_JW = ["Rings", "Necklace", "Collar", "Bracelet", "Earring"]
var type_ACS = ["Hair Wrap", "Ring", "Strap", "Sunglasses", "Crown"]
var type_LP = ["Waist belt", "Wallet", "Handbag", "Suitcase", "Face Mask"]
var type_Watches = ["classic", "Leather", "Smart", "Digital"]
var generatedTypes : [String] = [] { didSet{print("# Types updated ==> ( \(generatedTypes) )")} }
func updateTypesarray(){
if selectedSection?.name == "Trending Products" {
self.generatedTypes = type_TP
}
else if selectedSection?.name == "Accessories" {
self.generatedTypes = type_ACS
}
else if selectedSection?.name == "Featured Products" {
self.generatedTypes = type_FP
}
else if selectedSection?.name == "Watches" {
self.generatedTypes = type_Watches
}
else if selectedSection?.name == "Jewellery Products" {
self.generatedTypes = type_JW
}
else if selectedSection?.name == "Leather Products" {
self.generatedTypes = type_LP
}else { print("# ((Nothing Happaned!!))") }
}
#IBOutlet weak var pickertextField: UITextField!{
didSet{
pickertextField.inputView = UIView() //what a Magic!!!... This code solved Keyboard dismiss problem after assign the textField as FirstResponder!
pickertextField.delegate = self
pickertextField.allowsEditingTextAttributes = false
pickertextField.becomeFirstResponder()
}
}
#objc func textField_CatchedData(_ sender : UITextField) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "Type Name"), object: nil, userInfo: ["text" : sender.text!])
}
var productsList: [productObject] = []
var RecommendedArray: [String] = ["TH-1791721_side","Image-2","touq2","TH-1791349","watch2","eswara","Image-1",] //to be updated later!
}
extension SelectSectionVC : UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
func setupCell() {
CollectionView.delegate = self; CollectionView.dataSource = self
CollectionView.register(UINib(nibName: "SelectionCell", bundle: nil), forCellWithReuseIdentifier: "cell")
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: self.CollectionView.frame.size.width - 25, height: self.CollectionView.frame.size.height/4 - 0.5)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { // make spacing between each cell
return 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
sectionsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = CollectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SelectionCell
cell.isSelected = false
pickertextField.text = "Select product type" // Reset text to "Select product type" when press new cell"
cell.sectionName.text = sectionsArray[indexPath.row].name
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("# secArray Count = \(sectionsArray.count)")
self.selectedSection = sectionsArray[indexPath.row]
updateTypesarray()
print("# //Selected cell => TypesArray => \(generatedTypes)")
pickertextField.becomeFirstResponder()
}
}
The problem is: title & number Of Rows In Component are not getting the value from (generatedTypes) array after it get updated when cell did selected!
extension SelectSectionVC : UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return generatedTypes.count
}
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
return NSAttributedString(string: self.generatedTypes[row], attributes: [NSAttributedString.Key.foregroundColor: UIColor.yellow])
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return generatedTypes[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.pickertextField?.text = generatedTypes[row]
self.pickerView.isHidden = true
self.DoneBtn.isHidden = false
self.pickertextField.endEditing(true)
print("# pickerTextField... has ended editing!")
}
I did many many breakpoints and I'm sure that [generatedTypes] array has a value..but I don't know why it always called inside pickerView before ti get updated!
please help me
Thank you in advance
You may need to reload the picker end of updateTypesarray
func updateTypesarray(){
......
self.pickerView.reloadAllComponents()
}

Cannot convert return expression of type 'NSManagedObject' to return type 'String?'

I have built an app (my first) and it works like I want it to, but then I needed to add CoreData to it to get my events saved/fetched. I can see that my events (add information to tableview/remove information from there) gets saved as I print it, and the app is "Loaded" in "viewDidAppear".
The problem I think is that I am saving the array, not the NSobject. OR I am fetch the array, not the NSobject? I believe what I need to do is to add my array to an NSManagedObject?
Like this:
var List:[MealsMenu] = [].
When I do this I get it to work! Thats great! BUT, I have an VC that is calling this variable in a UIPickerView, and as soon as I Add the list to NSManagedObject (MealsMenu, which is my Entity in coredatamodel) I get this error-message:
Cannot convert return expression of type 'NSManagedObject' to return type 'String?'
I have tried to google this for a few days now but can't find any solution for this in my case. Do any of you have any idea what I can do?
My Entity (MealsMenu) have an attribute called "meals" which is refer to String.
As I said, If I skip the VC where the PickerView is I get it to work! Added and Deleted events are being saved.
Here are my code:
(All places where it says "List[row]" or "List[indexPath.row]" I get this error message).
addViewController:
import UIKit
import CoreData
class addViewController: UIViewController, UITextFieldDelegate {
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var input: UITextField!
#IBAction func addToMenu(_ sender: Any) {
if input != nil{
let newMeal = NSEntityDescription.insertNewObject(forEntityName: "MealsMenu", into: context)
newMeal.setValue(self.input.text, forKey: "meals")
do {
try context.save()
print("SAVED")
}
catch{
print(error)
}
input.text = ""
}else{
print("That's not a meal!")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
input.resignFirstResponder()
return true
}
}
tableViewController:
import UIKit
import CoreData
class tableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (List.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = List[indexPath.row]
cell.textLabel?.textColor = UIColor.white
cell.backgroundColor = UIColor.clear
return(cell)
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete
{
List.remove(at: indexPath.row)
myTableView.reloadData()
}
}
#IBOutlet weak var myTableView: UITableView!
override func viewDidAppear(_ animated: Bool) {
myTableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
coredataClass.saveItems()
coredataClass.loadData()
}
}
ViewController:
import UIKit
import CoreData
var List:[MealsMenu] = []
class ViewController: UIViewController {
#IBOutlet weak var monday: UITextField!
#IBOutlet weak var tuesday: UITextField!
#IBOutlet weak var wednesday: UITextField!
#IBOutlet weak var thursday: UITextField!
#IBOutlet weak var friday: UITextField!
#IBOutlet weak var saturday: UITextField!
#IBOutlet weak var sunday: UITextField!
var daysArray = [UITextField]()
let pickerView = ToolbarPickerView()
var selectedMenu : String?
override func viewDidLoad() {
super.viewDidLoad()
setupDelegateForPickerView()
setupDelegatesForTextFields()
coredataClass.loadData()
}
func setupDelegatesForTextFields() {
//appending textfields in an array
daysArray += [monday, tuesday, wednesday, thursday, friday, saturday, sunday]
//using the array to set up the delegates, inputview for pickerview and also the inputAccessoryView for the toolbar
for day in daysArray {
day.delegate = self
day.inputView = pickerView
day.inputAccessoryView = pickerView.toolbar
}
}
func setupDelegateForPickerView() {
pickerView.dataSource = self
pickerView.delegate = self
pickerView.toolbarDelegate = self
}
}
// Create an extension for textfield delegate
extension ViewController : UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
self.pickerView.reloadAllComponents()
}
}
// Extension for pickerview and toolbar
extension ViewController : UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return List.count
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return List[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Check if the textfield isFirstResponder.
if monday.isFirstResponder {
monday.text = List[row]
} else if tuesday.isFirstResponder {
tuesday.text = List[row]
} else if wednesday.isFirstResponder {
wednesday.text = List[row]
} else if thursday.isFirstResponder {
thursday.text = List[row]
} else if friday.isFirstResponder {
friday.text = List[row]
} else if saturday.isFirstResponder {
saturday.text = List[row]
} else if sunday.isFirstResponder {
sunday.text = List[row]
} else {
//log errors
}
}
}
extension ViewController: ToolbarPickerViewDelegate {
func didTapDone() {
self.view.endEditing(true)
}
func didTapCancel() {
self.view.endEditing(true)
}
}
Specific these functions/lines in "ViewController" are throwing theses errors:
Cannot convert return expression of type 'MealsMenu' to return type 'String?'
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return List[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Check if the textfield isFirstResponder.
if monday.isFirstResponder {
monday.text = List[row]
} else if tuesday.isFirstResponder {
tuesday.text = List[row]
} else if wednesday.isFirstResponder {
wednesday.text = List[row]
} else if thursday.isFirstResponder {
thursday.text = List[row]
} else if friday.isFirstResponder {
friday.text = List[row]
} else if saturday.isFirstResponder {
saturday.text = List[row]
} else if sunday.isFirstResponder {
sunday.text = List[row]
} else {
//log errors
}
}
And in "tableViewController" this function is throwing a similar error:
Cannot assign value of type 'MealsMenu' to type 'String?'
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
cell.textLabel?.text = List[indexPath.row]
cell.textLabel?.textColor = UIColor.white
cell.backgroundColor = UIColor.clear
Thanks a lot for any guidens and help!
/
Andreas
Please read the error message carefully and then have a look at you design
Your data source is an array of MealsMenu instances which is a subclass of NSManagedObject while the return value is of type String?.
That's what the error message says: You cannot return a MealsMenu instance where a string is expected
return List[row].meals
There are two bad practices in the data source declaration line.
Variable names are supposed to be named lowercased.
The variable name of an array is supposed to be named in plural form.
The latter would indicate clearly that the variable contains multiple items for example
var menus : [MealsMenu] = []
With this change and with splitting the line into two the error becomes more obvious
let menu = menus[row]
return menu.meals
By the way the naming meals is also confusing because it implies to be an array.
In cellForRow change the lines accordingly
let menu = menus[indexPath.row]
cell.textLabel?.text = menu.meals

How to delete a section and a row from a TableView with Realm and Swift?

ableView. I'm currently working on this gym app and I'm hoping to get a little bit of guidance here.
Basically what I want is to be able to swipe the cell and delete the row and the section associated with that row.
When I run my code, this is the error I get
'attempt to delete row 0 from section 1, but there are only 1 sections before the update'
Any help would be much appreciated.
class WorkoutsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SwipeTableViewCellDelegate, CollapsibleTableSectionDelegate {
let realm = try! Realm()
var workouts : Results<Workouts>?
var days : Results<WeekDays>!
var daysOfWeek : [String] = ["Monday", "Tuesday", "Wednsday", "Thursday", "Friday", "Saturday", "Sunday"]
let picker = UIPickerView()
#IBOutlet weak var WorkoutsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
WorkoutsTableView.delegate = self
WorkoutsTableView.dataSource = self
picker.delegate = self
picker.dataSource = self
loadCategories()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
tableView.rowHeight = 80.0
//Populate based on the # of workouts in each day.
let day = days[section]
return day.workouts.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return days[section].day
}
func numberOfSections(in tableView: UITableView) -> Int {
return days.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SwipeTableViewCell
cell.delegate = self
if (days?[indexPath.section]) != nil {
cell.accessoryType = .disclosureIndicator
//Populate with titles of workouts based on section/day of the week.
//cell.textLabel?.text = days?[indexPath.row].workouts[indexPath.row].name
cell.textLabel?.text = days[indexPath.section].workouts[indexPath.row].name
}
return cell
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
guard orientation == .right else { return nil }
let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
self.updateModel(at: indexPath)
}
// customize the action appearance
deleteAction.image = UIImage(named: "delete-icon")
return [deleteAction]
}
func tableView(_ tableView: UITableView, editActionsOptionsForRowAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
var options = SwipeOptions()
options.expansionStyle = .destructive
return options
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
#IBAction func AddWorkoutButton(_ sender: UIButton) {
var textField = UITextField()
textField.becomeFirstResponder()
let alert = UIAlertController(title: "New Workout", message: "Please name your workout...", preferredStyle: .alert)
let addAction = UIAlertAction(title: "Add Workout", style: .default) { (UIAlertAction) in
//Add workout to database
//Create a two dimensional array object in Realm with numbers corresponding to each day of the week.
//Append workouts to the day in the dictionary that the user selects.
let newWorkout = Workouts()
let dow = WeekDays()
dow.day = self.daysOfWeek[self.picker.selectedRow(inComponent: 0)]
newWorkout.name = textField.text!
dow.workouts.append(newWorkout)
self.save(newDay: dow)
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Muscle Group"
textField = alertTextField
alertTextField.inputView = self.picker
}
alert.addAction(addAction)
present(alert, animated: true, completion: nil)
}
func save(newDay: WeekDays){
do {
try realm.write {
realm.add(newDay)
}
} catch {
print("Error saving workout \(error)")
}
WorkoutsTableView.reloadData()
}
func updateModel(at indexPath: IndexPath){
if let workoutForDeletion = self.days?[indexPath.section]{
do {
try self.realm.write {
self.realm.delete(workoutForDeletion)
}
} catch {
print("Error deleting workout, \(error)")
}
}
self.WorkoutsTableView.reloadData()
}
func loadCategories(){
days = realm.objects(WeekDays.self)
WorkoutsTableView.reloadData()
}
#IBAction func EditWorkout(_ sender: UIBarButtonItem) {
}
}
extension WorkoutsViewController : UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 7
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return daysOfWeek[row]
}
}
class WeekDays : Object {
#objc dynamic var day : String = ""
let workouts = List<Workouts>()
}
class Workouts : Object {
#objc dynamic var name : String = ""
var parentDay = LinkingObjects(fromType: WeekDays.self, property: "workouts")
}
If you are trying to delete a whole section, then your code is correct. Try calling loadCategories instead of reloading your tableView to see if that helps. See updated code below:
Change that line to this instead:
func updateModel(at indexPath: IndexPath){
if let workoutForDeletion = self.days?[indexPath.section] {
do {
try self.realm.write {
self.realm.delete(workoutForDeletion)
}
} catch {
print("Error deleting workout, \(error)")
}
}
self.loadCategories()
}

Updating UIImageView without reloading all data

I have been stuck on this problem for a few days now. I have an image view set as invisible until the long press gesture is triggered. However, I can not wrap my head around on how to make the UIImageView to become visible after such. This is within a Cell in tableView. I have used reloadData(), but the issue is the table loses its place. It scrolls upwards by the time you release your finger. The long gesture triggers a boolean on the database side that toggles saveShow. When data is loaded, the query checks and displays the UIImageView if true. So I believe I would have to reload the data since it is based on the query..? Any help will be greatly appreciated!
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIPickerViewDelegate, UIPickerViewDataSource {
let db = DatabaseController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var cityPicker: UIPickerView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
cityPicker.delegate = self
cityPicker.dataSource = self
self.tableView.autoresizingMask = UIView.AutoresizingMask.flexibleHeight;
self.db.createTable()
//////////////////////////////////////////////////////////////////
let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPress))
longPressGesture.minimumPressDuration = 1.0 // 1 second press
longPressGesture.delegate = self as? UIGestureRecognizerDelegate
self.tableView.addGestureRecognizer(longPressGesture)
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////
Alamofire.request("http://url.com/jsonfile").responseJSON { response in
if (response.result.value != nil) {
let strOutput = JSON(response.result.value ?? "nil")
if let resObj = strOutput.arrayObject {
arrRes2 = resObj as! [[String:AnyObject]]
self.db.addShowsToDatabase(arrRes: arrRes2)
}
}
self.db.deleteOldShows()
self.grabData(id: 1)
self.tableView.reloadData()
}
////////////////////////////////////////////////////////////////////
}
func grabData(id: Int) -> Void {
regionResults = self.db.listAllShows(id: id)
self.tableView.reloadData()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return cities.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return cities[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
cheapFix = row + 1
grabData(id: row + 1)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if regionResults.isEmpty {
return 1
} else {
return regionResults.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bandCustomCell") as! BandTableViewCell
if regionResults.isEmpty {
cell.bandPic.image = UIImage(named: "flyer.jpg")
cell.bandSummary?.text = "No Shows Announced!"
cell.bandVenue?.text = "None"
cell.bandDate?.text = "None"
} else {
let show = regionResults[indexPath.row]
//print(try! show.get(Expression<String>("show_summary")))
let strDateFull = try! show.get(Expression<String>("show_date"))
var strDate: Array = ((strDateFull as AnyObject).components(separatedBy: "T"))
// Setting up the date
var dateArr: Array = strDate[0].components(separatedBy: "-")
let dateStr: String = "\(dateArr[1])-\(dateArr[2])-\(dateArr[0])"
// Setting up the time
var timeArr: Array = strDate[1].components(separatedBy: ":")
var timeStr: Int? = Int(timeArr[0])
// Changing military time to standard
if timeStr! > 12 {
timeStr = timeStr! - 12
}
let saved = try! show.get(Expression<Bool>("save_show"))
if (saved == true) {
cell.bandSavedShow.isHidden = false
} else if (saved == false) {
cell.bandSavedShow.isHidden = true
}
cell.bandSummary?.text = "\(try! show.get(Expression<String>("show_summary"))), \(timeStr!):\(timeArr[1])PM"
cell.bandVenue?.text = try! show.get(Expression<String>("venue_name"))
/*if (show["shows_img"] as? String != "null") {
let url = URL(string: show["shows_img"] as! String)
cell.bandPic.kf.setImage(with: url)
} else {*/
cell.bandPic.image = UIImage(named: "flyer.jpg")
//}
cell.bandDate?.text = dateStr
tableView.isScrollEnabled = true
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
myIndex2 = indexPath.row
//performSegue(withIdentifier: "segue2", sender: self)
}
#objc func longPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == UIGestureRecognizer.State.began {
let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
if let indexPath = self.tableView.indexPathForRow(at: touchPoint) {
let show = regionResults[indexPath.row]
let summary = try! show.get(Expression<String>("show_summary"))
let date = try! show.get(Expression<String>("show_date"))
let saved = try! show.get(Expression<Bool>("save_show"))
self.db.toogleSaveShows(summary: summary, date: date, saved: saved)
reload(tableView: self.tableView, indexPath: indexPath)
}
//grabData(id: cheapFix)
}
}
func reload(tableView: UITableView, indexPath: IndexPath) {
//found on stackoverflow
let contentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
tableView.setContentOffset(contentOffset, animated: false)
tableView.endUpdates()
}
}
update the longPressGesture method as below:
let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
if let indexPath = self.tableView.indexPathForRow(at: touchPoint) {
let cell = tableView.cellForRow(at: indexPath) as? BandTableViewCell;
cell.bandPic.image = ....//provide image here.
}
Here, it is not required to reload full table, instead just access the cell and set imageview there.
Firstly you must add gesture recogniser on the cell being used instead of the whole table view as in selector method of gesture you are trying to get the indexpath of cell being pressed.
Then try reloading the cell with found indexpath.
self.tableView.reloadRows(at: [indexpath], with: .automatic)

Resources