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
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?).
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.
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.
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.
}
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.