How to pushViewController from a custom UIView in Swift? - swift

I have a custom UIView and it will show when user go to specific UIViewController, on this UIView there is a button.
When user click on the button, I want to push a viewcontroller but I cannot call self.navigationController?.pushViewController(inputCode, animated: true)
How can I do the same behavior from a custom UIView? And also I am not using storyboard.

You can't access UIViewController from a custom UIView unless you pass its reference. Anyways, it wouldn't be nice if you do so. You can handle it by adding action from the UIViewController side
yourCustomView.yourActionButton.addTaget(self, action: #selector(self.tappedYourActionButton), forControlEvents: .touchUpInside)
#objc func tappedYourActionButton(_ sender: UIButton?) {
}

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

Bar button item on every view with specific controller function

I have to display a navigation bar button item on almost every view in the project.
I extended UIViewController
extension UIViewController{
func addNavBarItem(){
//add the bar button item
}
}
And I call addNavBarItem() on the viewDidLoad. However, the action taken when this button is tapped can be different for each view. Is there a way to set the target for this button to the actual view controller that called this method? Ideally I would want something like....
extension UIViewController{
func addNavBarItem(){
//do stuff
btn.addTarget(self, action: #selector(OriginalViewController.doSomething), forControlEvents: .TouchUpInside)
}
}
But OriginalViewController isn't available. Is there a quick solution or alternative pattern I should employ?
One solution that I use is to subclass UINavigationController and put my navigation bar item code in to the willShowViewController delegate method like so...
class MyNavigationController : UINavigationController {
override func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
super.navigationController(navigationController, willShowViewController: viewController, animated: animated)
let backButton = UIBarButtonItem(image: UIImage(named: "ic_menu"), style: UIBarButtonItemStyle.Plain, target: viewController, action: "doSomething")
viewController.navigationItem.leftBarButtonItem = backButton
}
}
This means when you add the target you can reference the exact viewController that is being shown by the navigation controller.

UITableViewCell's RemoveGestureRecognizer method doesn't work in Swift, ios8, Xcode 7

I have a UITableView with static cells and grouped style in my UIViewController. I added a UIGestureRecognizer, that I can dismiss the Keyboard, but I want to except the UIGestureRecognizer for several UITableViewCells, because they have a functionality when they get selected.
My code:
The class - variable that I can use it everywhere in the code:
var tap:UITapGestureRecognizer = UITapGestureRecognizer()
My viewDidLoad() Method:
override func viewDidLoad() {
super.viewDidLoad()
self.tap = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
tableView.addGestureRecognizer(tap)
}
And the Action of the UIGestureRecognizer:
func DismissKeyboard()
{
print("is here")
if(self.keyboardIsVisible)
{
view.endEditing(true)
self.refreshTableView()
}
}
I removed the Gesture Recognizer of one cell in the override func cellForRowAtIndexPath:
cell.removeGestureRecognizer(self.tap)
But when I tap this cell it still goes into the DismissKeyboard - Action...
You add your UITapGesgureRecognizer to tableView and you try remove from de cell. Try use tableView.removeGestureRecognizer(self.tap)
Instead of Adding Tap Gesture on TableView Cell or TableView. you can Add Custom UIButton on UITableViewCell and Add Target to it. You can then disable Table view's didSelectRowatIndexPath event. You can write your code on button click events.

Swift - UIStoryboardSegue pop up ViewController programatically

I have UIButton attached to navigationBar programatically:
catButton.addTarget(self, action: "oc:", forControlEvents: .TouchUpInside)
var catBarButton:UIBarButtonItem = UIBarButtonItem(customView: catButton)
self.navigationItem.setRightBarButtonItem(catBarButton, animated: false)
when function oc() is triggered I want to popover view controller
let segue = UIStoryboardSegue(identifier: "oc", source: self, destination: MDComDBCatsTVC())
prepareForSegue(segue, sender: but)
but this doesn't open MDComDBCatsTVC.. How to do it programatically because I can't drag from button in my storyboard because my button is added programatically
I can't drag from button in my storyboard because my button is added programatically
You can create the segue in the storyboard by dragging from the view controller icon to the target view controller. This segue can then be used programmatically.
Click on the segue to assign an identifier in the Attributes inspector panel. The segue can then be accessed programmatically using this identifier.
Xcode will insist that popover segues have an anchor. The error message is “Popover segue with no anchor”. This can be resolved in the Attributes inspector - the Anchor property. Drag from the circle to your view.
In the action for your programmatically created button you will then be able to access the segue using eg:
func buttonAction(sender:UIButton!)
{
performSegueWithIdentifier("popoversegue", sender: self)
}
( ... or more likely something more robust using if let etc)
you can use dynamic popup views like that
let vc = storyboard.instantiateViewControllerWithIdentifier("myPopupView") as! myPopupViewViewController
self.presentViewController(vc, animated: true, completion: nil)
If you want to do this via storyboard connect your view controllers in storyboard with segue (drag from first view controller to second one, it doesn't mean to drag from button only) name that segue "oc" then in button action perform segue like following
performSegueWithIdentifier("oc", sender: but)

Swift iOS: Perform a Segue from an Instance in a ViewController to another ViewController

First of all, I have to say that there is so much information on this platform, because of everyone, who is helping. Thank you very much.
At the moment, I have reached a point, where I am not able to go further on my own.
This is my problem:
I have a MainScreen ViewController which is embeded in a Navigation Controller.
From this MainScreen, I can show with a Segue a GameViewController which creates a Gamescene.
In this Gamescene, I have a button, which when it is tapped, shows a Popup and pauses the Gamescene.
This Popup contains two buttons:
A button to resume the game and make the popup disappear.
A button which should end the Gamescene and return to the Mainscreen.
And here is my problem.
I am not able to create the function for the End Button. I have tried different constellations with segues, but it didn't end well.
I think that the difficulty here is, that this button is in an separate class. The hierarchy here, as I understand it, would be:
GameViewController -> GameScene -> Popup -> EndButton.
Do you have any ideas how I could solve this problem? Would there be an other way to solve this transition without using segues?
Thank you very much for your help in advance!
Kind regards,
Phil
Update:
In my Gamescene the popup basically looks like this:
class GameScene: SKScene, SKPhysicsContactDelegate {
// ....
var pauseButton : UIButton! = UIButton()
var pausePopup : PausePopup! // my Popup class with the End button
// ....
override func didMoveToView(view: SKView) {
//configuring the button
pause_button.addTarget(self, action: "pauseButtonPressed", forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(pause_button)
}
func pauseButtonPressed() {
self.pausePopup = PausePopup(nibName: "uxPausePopup", bundle: nil)
self.scene!.view!.paused = true
self.pausePopup.showInView(self.viewPlaceHolder, animated: true, scene: self)
}
// this is where my popup appears.
In my Popup class I have an action for the EndButton which is located on the Popup and from which I would like to return to the main screen:
#IBAction func showMainScreen(sender: AnyObject) {
// this does not work ...
self.performSegueWithIdentifier("showMainScreen", sender: nil)
}
You cannot call other view from showInView, you need to dismiss the popOver and call the mainView from GameScene, to do so you can create a competition handler in your PausePopup or a protocol to pass the result of your popOver to GameScene deal with the result