How to fix segue showing a grey screen - swift

I wanted to create a segue going from FirstViewController to DetailViewController, but when I write
let detailVC = DetailViewController()
detailVC.result = indexPath.row
print(detailVC.result)
self.present(detailVC, animated: true, completion: nil)
, all it presents is a grey/transparent screen. How can I fix this?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostTableViewCell
cell.typeLabel.text = "Name: \(posts[indexPath.row].typeOfFood)"
cell.titleLabel.text = "Title: \(posts[indexPath.row].title)"
cell.locationLabel.text = "Location: \(posts[indexPath.row].location)"
cell.nameLabel.text = "Name: \(posts[indexPath.row].name)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// print("Select Row \(indexPath.row)")
var result = indexPath.row
tableView.deselectRow(at: indexPath, animated: true)
// self.performSegue(withIdentifier: "showDetail", sender: nil)
let detailVC = DetailViewController()
detailVC.result = indexPath.row
print(detailVC.result)
self.present(detailVC, animated: true, completion: nil)
}
} ```

The problem is the phrase DetailViewController(). That's not you get the detail view controller you designed in the storyboard.
Instead, give the view controller you designed in the storyboard an identifier, and ask the storyboard for that view controller by calling this method:
https://developer.apple.com/documentation/uikit/uistoryboard/1616214-instantiateviewcontroller
Now cast that view controller down to DetailViewController and go from there.

Related

Missing return in a function expected to return 'UITableViewCell' ; please help me

extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.deselectRow(at: indexPath, animated: true)
let vc = storyboard?.instantiateViewController(identifier: "tasks") as! TaskViewController
vc.title = "New Tasks"
vc.task = tasks[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
//error:Missing return in a function expected to return 'UITableViewCell'
}
}
I try diffrence of return type but none of them are correct
The process is wrong in this function you are supposed to create a cell instance and work with it to be able to return it
Code modified:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/// Create a file for the cell let's say call it `myCell` with a label for task "let's suppose called `lblTaskOutlet`
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! myCell
myCell.lblTaskOutlet = tasks[indexPath.row]
return cell
}
}
You can add this vc.title = "New Tasks" in your viewDidLoad() :
self.title = "New Tasks"
The below line is another function called alone with its own body:
tableView.deselectRow(at: indexPath, animated: true)
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method is a tableview delegate method which populates the cells for each row. It is expecting a return type of UITableViewCell.
You can create UITableViewCell subclass TaskCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/// Create a file for the cell let's say call it `TaskCell` with a object for task "let's suppose called `taskObj`
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
cell.taskObj = tasks[indexPath.row]
return cell
}
For the click action on cell you have to override another UITableViewDataSource method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "tasks") as! TaskViewController
vc.title = "New Tasks"
vc.task = tasks[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
}
And please make sure that you have set tableview delegate and datasource in view did load of your view controller.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}

How to disable/enable notifications in tablewiew by using UISwitch in Swift

I am working on a location-based reminder app. I show all reminders that user created on a table view. I have also UISwitch on every cell. I want that UISwitch disables/enables reminders individually, not all notifications. I couldn't figure it out.
extension MainViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
//switch
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = indexPath.row
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
cell.accessoryView = swicthView
let itemToRemove = self.items![indexPath.row]
let notifToRemove: String = itemToRemove.notifID!
return cell
}
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
}
I believe your code is not working because cells are reused and actions on specific cells should be handled from the cell class rather than from ViewController. Move this code into the UITableViewCell code.
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = switchViewTag!
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
and add a new property in the UITableViewCell
weak var switchViewTag: Int?
Modify your cellForRowAt delegate method to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
cell.switchViewTag = indexPath.row
return cell
}

Swift progressBar filling on every cell-click

Each of my cells in tableView has its own progressBar which is being filled in cellForRowAt method and it works fine.
My problem is whenever I select a cell, it's progress bar refreshes filling.
Does anyone have an idea why and how to prevent it? I think didSelectRowAt method is not a reason because that method does nothing with the progressBar.
Method for filling progressBar:
func updateProgressBar(_ cell: TestTableViewCell, level: String) {
let cellCards = Float64(self.getNumberOfCardsForLevel(level: level))!
let totalCards = Float64(self.totalCards())!
delay(0.3) {
cell.cellProgress.setProgress(Float(cellCards / totalCards), animated: true)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell
cell.accessoryType = .disclosureIndicator;
cell.cellProgress.progress = 0
cell.cellTitle.text = "Test"
cell.numOfCardsInCell.text = getNumberOfCardsForLevel(level: "1")
updateProgressBar(cell, level: "1")
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.indexPathsForSelectedRows?.forEach { index in
if(self.cellSelectedImage[indexPath.row] == UIImage(named: "imageTwo") ) {
tableView.deselectRow(at: indexPath, animated: false)
self.cellSelectedImage[indexPath.row] = UIImage(named: "imageOne")
}
else {
print("Selected row -> \(index.row)")
if self.cellSelectedImage[indexPath.row] == UIImage(named: "imageTwo") {
self.cellSelectedImage[indexPath.row] = UIImage(named: "imageOne")
}
}
}
tableView.reloadRows(at: [indexPath], with: .automatic)
}

perform a root viewcontroller segue programmatically on a tableviewcell in swift

The code below creates a tableviewcell and places a object in it. The code to place data inside the tableviewcell is not present. All I want to do is when the user clicks on the tableviewcell it to be transfers to a basic view controller class. No need to show how to go back.
var itemName : [NSManagedObject] = []
var theField = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
theField.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let title = itemName[indexPath.row]
let cell = theField.dequeueReusableCell(withIdentifier: "MyCell", for : indexPath)
cell.selectionStyle = .default
let attr1 = title.value(forKey: "name") as? String
let text = [attr1].flatMap { $0 }.reduce("", +)
cell.textLabel?.text = "\(text)"
cell.textLabel?.textAlignment = .center
cell.layoutMargins = UIEdgeInsets.zero
cell.preservesSuperviewLayoutMargins = false
cell.separatorInset = UIEdgeInsets.zero
cell.layoutMargins = UIEdgeInsets.zero
return cell
}
#objc func moveRight() {
//pass tableview cell
let vce = fullScreen()
vce.text = UITableViewCell
vce.modalPresentationStyle = .overCurrentContext // actually .fullScreen would be better
vce.present(vce, animated: true)}
Assume you have an array titles
cell.textLabel?.text = titles[indexPath.row]
and then take action in this method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
moveRight(titles[indexPath.row])
}
func moveRight(_ title:String) {
//pass tableview cell
let vc = fullScreen()
vc.text = title
self.navigationController!.pushViewController(vc, animated: true)
}
You need to implement the UITableViewDelegate protocol. So:
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { .. }
}

Navigate from a ViewController to an another on didSelectRowAt programmatically

I have a UIViewController that contains a custom UITableView. The table has a custom UITableViewCell too.
How to navigate from the first ViewController to an another when you select/click one of the rows?
Note
I have not used StoryBoard.
Update
This my code. Each one of the classes are external file. Let me know, if you need more code.
class TestViewController: UIViewController, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(testTableView)
}
let testTableView: TestTableView = {
let table = TestTableView()
table.register(TestTableViewCell.self, forCellReuseIdentifier: TestTableViewCell.identifier)
return table
}()
}
class TestTableView: UITableView, UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.identifier, for: indexPath)
return cell
}
}
class TestTableViewCell: UITableViewCell {
static let identifier = "testCell"
}
Here is a complete answer:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newViewController = NewViewController()
self.navigationController?.pushViewController(newViewController, animated: true)
}
In UIKit in order to programmatically navigate from TableCell you can do it without storyboard.
You have two ways to view next viewController
If you want to present .......
animating from bottom to top
(one thing to remember) you can not present new view without dismissing previous presented screen, otherwise app crash due to conflict of presented screen
yourTableView.deselectRow(at: indexPath, animated: true)//used for single Tap
let vC = YourViewController (nibName: "" , bundle : nil)
vC.modalPresentationStyle = .fullScreen //this line is optional for fullscreen
self.present(vC, animated: true , completion: nil)
Or if you want to View your viewController normally (2 is batter) ....
animate from right to left
yourTableView.deselectRow(at: indexPath, animated: true)
let vC = YourViewController()
self.navigationController?.pushViewController(vC, animated: true)
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = yourCustomVCName (nibName: "yourCustomVC nibName" , bundle : nil)
self.present(vc, animated: true , completion: nil)
}