Present Pop-up UIAlertController from within UITableViewCell Without UITableView - swift

In my iOS app I have a UIViewController ViewController that presents a view that is partly made up of a TableView. The UITableViewCells include a UISegmentControl. When the UISegmentControl's valueHasChanged action is called, I want to present a pop-up. This is the code called within the action:
//Check that the text field isn't empty:
if self.TextField.text == "" {
self.Status.selectedSegmentIndex = 0
let viewController = ViewController()
viewController.alertStatusChangedForEmptyTextField(self)
}
This is the pop-up I have created within the ViewController class that checks if an adjacent text field in the same UITableViewCell is empty or not:
//Alert function that handles when toggle is set even though there is no content.
func alertStatusChangedForEmptyTextField(sender: AnyObject) {
let alert = UIAlertController(title: "Text field has no contents", message: "Please enter a text.", preferredStyle: UIAlertControllerStyle.Alert);
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Cancel, handler: nil));
showViewController(alert, sender: self);
}
I get an error that pops up in the viewDidLoad function of the ViewController, but originates from the showViewController method call within this function (I know because I've tried commenting out only this last line and then everything works fine).
How can I call a pop-up(ViewController) from within a UITableViewCell? Happy to hear suggestions that involve completely different approaches (Creating a custom UITableView subclass? A function that is defined within the TableViewCell class?)
Also, I don't have a root view controller.

check the value of storyboard, there is a good chance that you will find it nil;
storyboard.instantiateViewControllerWithIdentifier("PopMenuViewController") as! PopMenuViewController
I am trying to figure out how to present the pop up from custom tableview cell element. If you figured it out than do share the method :)

Related

Programmatically press back button for UIViewController with UITableView iOS swift

I have a UIViewController that implements UITableViewDelegate, UITableViewDataSource and that contains a UITableView as a member variable. When a user click on one of the rows of that table, the app performs a storyboard segue to open the detail view controller. That detail view controller of course has a button in the top left of the screen that is the "back" button to go back up to the UIViewController with the UIViewTable.
So, suppose that I want to programmatically "click" that back button. How exactly would I do that in swift? This is the most recent version of swift (swift 4?) in XCode 10.1.
UPDATE:
So here is how I solved this. As the answers below show, it is possible to use self.navigationController?.popViewController(animated: true) to just return to the previous view controller. What I discovered I also wanted to do, however, was to call a specific method in that view controller so that it executed a certain behavior once it got shown. It turns out that is also possible, but in my case it was a bit tricky, since that prior view controller was actually a UITabBarController. Therefore I had to get the ViewController that I was interested in from the UITabBarController. I did it like this:
let numvc = navigationController!.viewControllers.count
let tvc:UITabBarController = navigationController!.viewControllers[numvc-2] as! UITabBarController
let my_vc: MyCustomVC = tvc.viewControllers![0] as! MyCustomVC
my_vc.some_function()
Here of course MyCustomV is my custom view controller class and some_function() is the method I want to call on that class. Hope this helps someone.
When You run a segue you perform a "pushViewController" method to the next view, so if you want to go back to the previous view programmatically you just have to do is pop the last view like so:
self.navigationController?.popViewController(animated: true)
UPDATE
You just need the if statement if you have multiple segues from that viewController, if not, you can delete and just cast the next view as you wish and set the properties, let the autocomplete write the *prepare(for segue... * method for you, so You don't run into any problems
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourSegueName" {
let destinationVC = segue.destination as! CustomViewController
destinationVC.labelExample.text = "Some text I'm sending"
}
}
Are you sure you need to "click" the button?
If all you need is to dismiss details view controller, you can just call navigationController?.popViewController(animated: true)
Or if you want to deal directly with button, you can tell it to send its actions: backButton.sendActions(for: .touchUpInside)
Or if you absolutely need to show button clicking animation, then you will need something like this (you should play and choose suitable delay):
backButton.isHighlighted = true
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
backButton.isHighlighted = false
backButton.sendActions(for: .touchUpInside)
}

NSUserDefaults changing every segue

Sorry for the newbie question but i am really stuck.
I have a UIViewController and 4 tableviews in an app. When i click on a button on the UIViewcontroller it segues to a UITableviewController called "Beach". When the user clicks on a cell of the table, it segues back to the UIViewController and displays the selected cells title as the buttons title. The problem that i am having is when i click on a nother button to a tableview and then clicks on the cell, the previous buttons title sets back to the previous title.
i have a prepare for segue function in the tables view controllers and this returns the selected table title (named : Titleoftable) to the main VC which, the strings.
the way i am currently doing it is to make a NSUserDefault below but the problem still remains the same - The value changes top "" every time i click on another table V---
let path = tableView.indexPathForSelectedRow
let cell = tableView.cellForRowAtIndexPath(path!)
let persistenceStoreKey = "MyStringKey"
let stringToStore = "\((cell?.textLabel?.text!)!)"
// Store data
NSUserDefaults.standardUserDefaults().setObject(stringToStore, forKey: persistenceStoreKey)
NSUserDefaults.standardUserDefaults().synchronize()
// Get data
let myStringt = NSUserDefaults.standardUserDefaults().stringForKey(persistenceStoreKey)
destination.textOfBeach = (myStringt)!
destination.isBeachSelected = true
}
I have been stuck on this problem for ages now! PLEASE HELP!!
PS- I am using swift-2 and Xcode7
Make sure that you are not pushing a new ViewController after you click on the tableview's cell. It gives me the impression that it might be the case since you are seeing only the title of the last cell you pressed.. Make sure you actually pop the tableview's controller instead of pushing a new one.
If you want to keep using NSUserDefaults, you could just call
navigationController?.popViewController(animated: true)
in your cellSelection function, and read the defaults value in the viewWillAppear of the main ViewController.
override func viewWillAppear(_ animated: Bool) {
let userDefault = NSUserDefaults.standard()
if let beachSelection = userDefault.string(forKey: "BeachControllerSelection") {
textOfBeach = beachSelection
isBeachSelected = true
}
//same for the rest of the other table view's selections
}

UIView is nil after moving to the selected index via UITabBarController

I am using a UITabBarController in a music playing app. I have 2 indexes in my tabBarController, index 0 (tableViewController) and index 1 (nowPlayingViewController). Index 0 contains a UITableViewController that displays song names, and index 1 contains a UIViewController that is supposed to display the now playing page, including labels of the current playing song and artist.
When a user taps on a cell (a song name) on tableViewController i do two things, first I give the title of the song that was selected to a third class, a ControllerClass and then I move the tab bar over to index 1, which is the now playing page...
//this is inside tableViewController class
let _ControllerClass = ControllerClass()
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
_ControllerClass.nowPlayingSongInfo(trackTitles[indexPath.row])
self.tabBarController?.selectedIndex = 1
}
So far so good. The nowPlayingViewController is the delegate of the ControllerClass. When ControllerClass receives the new song title, it calls a delegate method that is supposed to set the text of a UILabel on the nowPlayingViewController. Here is the method inside ControllerClass that calls back with the new title as a String.
func newNowPlayingSongTitle(songTitle: String){
delegate?.configureLabels(songTitle)
}
this also works fine, all good. I receive the callback successfully on nowPlayingViewController which I know because I am able to print the song title when the callback is received, like so...
func configureLabels(songTitle: String){
print("song title is \(songTitle)")
//successfully prints the correct song title
}
HOWEVER, my issue is this... I need to not just print out the new song title, I need to set my UILabel's text property on my nowPlayingViewController equal to the new song title that i receive in the callback. BUT, when i try this...
func configureLabels(songTitle: String){
print("song title is \(songTitle)")
self.songLabel.text = songTitle
//crashes on the line above error = "unexpectedly found nil while unwrapping an optional value"
}
the app crashes due to unexpectedly found nil while unwrapping... . Apparently the UILabel is nil, even though I know i have set it up properly since I can set its text property from other places.
I also tried this,
func configureLabels(songTitle: String){
let view = UIView(frame: CGRectMake(100, 100, 100, 100))
self.view.addSubview(view)
}
to see if the entire view itself is nil, and again it crashes with the same error, so indeed self.view is nil.
What is going on here? I am able to set the labels and images in this view from elsewhere, however in the situated illustarted above the UILabel and the entire view itself are nil. How to fix this problem? Any suggestions would be great, thank you.
Note: I have tried self.loadView() but that did not help either
My issue was that my delegate was set to a different instance of nowPlayingViewController, than the instance that was currently in the tabBarController. To fix this, instead of doing
let np = nowPlayingViewController()
ControllerClass.delegate = np
I did
let np = self.tabBarController?.viewControllers![1] as! nowPlayingViewController
then ControllerClass.delegate = np
the reason this is necessary with a UITabBarController is because the views that are embedded within the tabBarController are loaded once and then remain loaded, thus I needed to set the UILabels on the nowPlayingViewController for the proper instance of the nowPlayingViewController, which is the one that was embedded in the tabBarController, since that instance was the one being displayed. Sure hope someone else finds this useful!

Warning: Attempt to present ViewController on ViewController which is already presenting ViewController

I have a view controller with a toolbar with 3 UIButtons that open a new view controller as a popover. I created the segues in Storyboard and selected "Present as Popover". The popovers work but when the user taps on another button while a popover is currently open, I get this error:
Warning: Attempt to present <Fingerpainter.OpacityViewController: 0x79095110> on <Fingerpainter.DrawingViewController: 0x7b278000> which is already presenting <Fingerpainter.BrushSizeViewController: 0x79573770>
Is there a way to like make sure all popovers are closed before opening a new one? Here's my prepareForSegue method in the main ViewController (containing the toolbar):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let identifier = segue.identifier ?? ""
let popoverPresentationController = segue.destinationViewController.popoverPresentationController
popoverPresentationController!.delegate = self
switch identifier {
case Storyboard.BrushSizeSegueIdentifier:
if let brushSizeViewController = popoverPresentationController?.presentedViewController as? BrushSizeViewController {
// set properties in brushSizeViewController
}
case Storyboard.OpacitySegueIdentifier:
if let opacityViewController = popoverPresentationController?.presentedViewController as? OpacityViewController {
//set properties in opacityViewController
}
case Storyboard.ColorSegueIdentity:
if let colorViewController = popoverPresentationController?.presentedViewController as? ColorViewController {
//set properties in colorViewController
}
default:
break
}
}
Is there a way to like make sure all popovers are closed before opening a new one
It's the other way around. It's your job to make sure that while the popover is present, a button that summons another popover is not tappable. You can do this by disabling the button, but more commonly, in order to coordinate the disabling of the button with the presence of the popover, it's done by adjusting the popover presentation controller's passthroughViews.
Unfortunately there's a massive and long-standing bug where even setting the passthroughViews to nil doesn't prevent toolbar buttons from being tappable. The workaround is to do it with a delay. A lot of my popover code adds this sort of thing:
if let pop = popoverPresentationController {
delay(0.1) {
pop.passthroughViews = nil
}
}
(where delay is described here: https://stackoverflow.com/a/24318861/341994).

Swift open a new view controller programmatically on top of a map from a button insider a popover and a cell

I have a bit of an odd problem. Note that everything is done programmatically, so please no storyboard suggestions. Here's the setup:
I have a Master-Detail view layout, where the Detail view is a map. The Master-view's table is filled with cells that represent annotation points on the map. When you tap on a cell, it focuses on that annotation on the map and opens a custom annotation view using a popover. Likewise, when you tap the annotation itself, the same popover opens. So far, so good.
Now, inside the popover and inside each cell there is an a button which, when pressed, should open a new view that fills the whole screen. The view would have information specific to its corresponding annotation.
Here is how I am trying to do it:
Class that has the map (MyDetailViewController)
public func openNewViewController(){
self.presentViewController(NewViewController(), animated: true, completion: nil)
}
Function call for cell button and popover button
func btnPressed(sender: AnyObject){
var dvc = MyDetailViewController()
dvc.openNewViewController()
}
When either button gets tapped, I get the same error:
Warning: Attempt to present NewViewController on DetailViewController whose view is not in the window hierarchy!
So, what does it mean that DetailViewController is not in the window hierarchy..? It must be since I see it, no?
Any idea how to fix this issue? Thanks in advance!
Your function openNewViewController() is doing nothing!
See the code
self.presentViewController(openNewViewController()) , animated : true , completion: nil)
is pointing to itself.
Instead try this :
public func openNewViewController(){
var newController = UIViewController() // or any other view you wish to instantiate
self.presentViewController(newController, animated: true, completion: nil)
}