'Disable' segue for post with a conditional value iOS/ Swift - ios

here is my situation,
on my app, I get a list of post being display by a tablew view. on click, it bring to a postdetail view via this segue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath.row
self.performSegue(withIdentifier: "push", sender: self)
self.feedTableView.reloadData()
}
With Firebase, some of my post has a condition published with boolean value ( true / false )
I 'd like somehow, if the value is on false, to disable the segue. As some posts have a 'COMING SOON' title, nothing should happen on click of those posts.
Do you know how is that possible to do by any chance ? Will be lovely !
Thanks a lot !
EDIT --
That the structure of my DB where you can see the published condition

Try this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard posts[indexPath.row].title != "COMING SOON" else {
return
}
self.performSegue(withIdentifier: "push", sender: self)
}

I don't think you can "disable" a segue, but you can prevent it to be performed using a conditional if statement in your didSelectRowAtIndexPath delegate method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath.row
// get post info
let post = myArrayOfPosts[selectedIndexPath]
// check if post is COMING SOON
if post.title != "COMING SOON"{
// if not, trigger the segue
self.performSegue(withIdentifier: "push", sender: self)
}
self.feedTableView.reloadData()
}
I would recommend to store the "COMING SOON" string value in a constant in your class, or maybe in an Enum if you have other titles for which you want a different behavior.
EDIT: I've fixed the if statement which I incorrectly wrote in the first place. I cannot comment on posts yet (lack of reputation), so hopefully you see this edit :)

Related

How can I pass data from "didSelectRowAt" to another VC without presenting it or a segue?

What I basically want is I want to press on a row in my TableViewController which then takes, for example, the text that the row has and passes it to a ViewController that is currently not present. Like when you click on a song in the Spotify app and it plays it without presenting anything but the song details are shown in the mini-player.
Does anyone have a clue how to do that?
Well you can do that but you need to initialize your View Controller first for you to access its properties like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DestinationVc") as? DestinationVc
vc?.text = "TextToBePassed"
}
And there it is without presenting it but I don't get it why you don't want to present or show it but that's how it is done based on your question. Thanks :)
If you have created the UI via XIB or programatically then follow this approach:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let nextVC = NextViewController()
nextVC.text = model?[indexPath.row].text // If you are using model or if you have stored the data in the array then nextVC.text = yourArray[indexPath.row]["text"]
self.navigationController?.pushViewController(nextVC,animated: true)
}
Also, in the NextViewController, you have to add text. Like this:
class NextViewController: UIViewController {
var text = String()
//MARK:- View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
print(text) //This will print the text passed from previous VC
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = NextViewController()
vc.title = "\(Title)"
self.navigationController?.pushViewController(vc,animated: true)
}
Using this method can help you in passing the data to another Controller without using segue.
Assuming this is for iOS as the question doesn't specify.
In the tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) method in your UITableViewDelegate, you can grab the cell's text with
let cell = tableView.cellForRow(at: indexPath)?.textLabel?.text // cell: String?
You can then assign this to a global variable if you wish, or if you maintain a reference to the non-present view controller, you can give it a property to store this text and assign it before you call performSegue(withIdentifier identifier: String, sender: Any?).

Select a specific viewController in UITabBarController without knowing its index

I have a UItabBarController (called tabBarController) composed of a number of options. I also have a UITableView, whose first row is an option that should make the user navigate to a specific viewController.
My didSelectRowAt delegate method looks something like this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {
/* BrowseViewController is currently the second item in the
tabBarController, so I just select its index to navigate to it */
tabBarController?.selectedIndex = 2
}
}
Now, this works for my current situation because I know that the second item in tabBarController is the UIViewController that I am looking for, but I want to future-proof my app so that if the order of viewControllers in tabBarController is changed in the future, the tableView does not break.
In other words, I am wondering if there is a way to first extract the index of the viewController I am looking for from tabBarController, and then use that index to navigate to it, something like so:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {
let browseViewControllerIndex = Int()
/* iterate through tabBarController's VC's and if the type of the VC
is BrowseViewController, find its index and store it
in browseViewController */
}
}
You can try something like:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {
// safely get the different viewcontrollers of your tab bar
// (viewcontrollers is an optional value)
guard let tabs = tabBarController.viewcontrollers else { return }
// index(of:) gets you the index of the specified class type.
// Also an optional value
guard let index = tabs.index(of: BrowseViewController()) else { return }
tabBarController?.selectedIndex = index
}
}
I was able to figure this out on my own. The code below is what works best for my goals. It is reasonably concise and elegant (in my opinion):
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {
if let browseIndex = (tabBarController?.viewControllers?.filter { $0 is UINavigationController} as? [UINavigationController])?.firstIndex(where: { $0.viewControllers.first is BrowseViewController }) {
tabBarController?.selectedIndex = browseIndex
}
}
}
Note that BrowseViewController is the first viewController of UINavigationController. Of course, users looking at this answer should modify their code to suit their architecture.

tableview item looping through all items before displaying selected item siwift

I have an application where I receive information via Alamofire and it gets passed into a tableview. I can further get more details about selected products which is passed to a view controller. this works fine but the issue I have now is when I click a cell to get more details about a particular item it always has to navigate through all other items. If I click an item on cell 5, the detail viewcontroller would display from item 1,2,3,4 then 5 although it does it quickly. I believe once I close the modal like
#IBAction func closeModalPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
it is suppose to remove the previous details. how do I do it to make it show item 5 without looping through 1-4 first
did select row
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = Services.instance.items[indexPath.row]
Services.instance.selected = selected
let index = IndexPath(row: indexPath.row, section: 0)
tableView.reloadRows(at: [index], with: .none)
tableView.selectRow(at: index, animated: false, scrollPosition: .none)
NotificationCenter.default.post(name: NOTIFY, object: nil)
performSegue(withIdentifier: TO_DETAILS, sender: nil)
}
I don't think you are doing any api calls or anything in didSelectRowAt, I would suggest try following,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = Services.instance.items[indexPath.row]
Services.instance.selected = selected
NotificationCenter.default.post(name: NOTIFY, object: nil)
performSegue(withIdentifier: TO_DETAILS, sender: nil)
}
You are unnecessarily reloading row here and asking table view to select same row again which ultimately calls didSelectRowAt.

swift #IBAction from tableViewCell

I need to trigger a function by clicking on a tableViewCell,
until now I used #IBAction, but that option is only available with button type (I haven't found another way..)
this is the way I now of:
#IBAction func springPrs(_ sender: Any) {
//doing stuff..
}
but now I have an #IBOutlet
#IBOutlet weak var nextTrackCell: nextTableViewCell!
and I want to trigger a function by clicking on it. Any help?
This is a wrong approach, you should implement a delegate method from UITableViewDelegate called didSelectRowAt:
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//do your stuff here.
}
You shouldn't add an action directly to a table view cell because it violates the MVC design pattern and there is a handy callback already built into UITableViewDelegate to make this really easy.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
// do something when the top row tapped
} else {
// do something when any other row is tapped
}
}
You could also declare a closure inside the cell, this is what I tend to use when having to pass some action to the view controller.
var onButtonPressed: (() -> ())?
#IBAction func buttonPressed(_ sender: Any) {
onButtonPressed?()
}
And use it like so in cellForRowAt:
cell.onButtonPressed = { [unowned self] in
// Do what you need to, no need to capture self however, if you won't access it.
}

segue not called immediately

I have a view controller A and it has a table list created dynamically with cells
when calling didselectcell as
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if(cell?.tag == 1){
performSegueWithIdentifier("profile", sender: self)
print("perform segue for favourite")
}
and i am calling prepare for segue like this and saving the variable the variable is available in 2nd view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue == "profile"){
let vc = segue.destinationViewController as! MembersViewController
vc.title = " "
}
when i run the app it takes hardly more than 25 sec [ the second view controller has collectionview] any idea of doing it faster
I solved this by calling dispatch_async function it moves immediately to next page and the next page also has for loop in viewwillappear thanks #rakeshbs got answer from this link
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if(cell?.tag == 1){
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("profile", sender:self)
})
}
There are a few things wrong with this. Change your didSelectRow method to this.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if(indexPath.row == 0) {
performSegueWithIdentifier("profile", sender: nil)
}
}
You can fix your didSelectRowAtIndexPath method to remove needing to grab the cell. It has a reference to the cell's index already, which is what you were grabbing.
Also, your prepareForSegue method can be improved as follows:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "profile") {
let vc = segue.destinationViewController as? MembersViewController
vc?.title = "New Title"
}
}
You should never need to call dispatch_async with something like this, especially in Swift, unless you're already on a Background thread.
Before using the UITableView object and its UITableViewDelegate and UITableViewDataSource components, I suggest you muck about in an Xcode Playground, and read the Apple Documentation carefully to understand how everything works.

Resources