How can i navigate to mainviewcontroller from subview - uiview

I have just started working with swift, i have created a subview which has a button on it, i would like to use that button to take me to my mainviewcontroller.
i have used same functionality for a different button however having a function in same file allows that button to work the code is below
var playAgainButton = UIButton(frame: CGRectMake(0, 150, 320, 40))
playAgainButton.setTitle("Play Again", forState: UIControlState.Normal)
playAgainButton.addTarget(self, action: Selector("startGame"), forControlEvents: UIControlEvents.TouchUpInside)
playAgainButton.backgroundColor = UIColor.redColor()
gameOver.addSubview(playAgainButton)
is it possible to use similar code to navigate to a different viewcontroller?
Thanks

Are you using storyboards, or is this all in code? If it's in code, create a method like
func goToMainVC() {
if let navController = self.navigationController {
navController.popToRootViewControllerAnimated(true)
}
}
and set the button's target to a selector that calls it.
If you're using storyboards, you have three options:
ctrl+drag a connection from your button back to the main view controller (easy, but bad form because it just pushes the main VC back onto the nav controller);
Add #IBAction before the fun goToMainVC() method above, then ctrl+drag a connection from your button to the view controller in which it's contained, and then select that outlet method (this is how most people would do it); or
The best option is to use an unwind segue, as described here.

Related

Swift: Adding floating button to TableViewController?

I'm having trouble at TableViewController.
I want to add floating button, but I found out that if I create tableview with TableviewController in Storyboard, then tableview is superview in that view controller, which means only way to add button is adding button in tableview as one of a cell, which is not floating button. (Maybe I'm wrong. I'm a bit confused. I can't add another view by Storyboard.)
I googled several times and I think the only solution is to add button by using UIWindow, but part of the solution codes are deprecated.
I hope I can get alternate solution for my problem.
Obviously the best solution is using UIViewController and adding UITableView and your button as subviews (as #Surjeet Singh suggested in comment). However if you face troubles doing this (maybe too complex right now), you can add UIButton as subview of your keyWindow as workaround. however keep in mind that you need to manually remove the button from keyWindow once your UITableViewController is going to disappear, or else your button will be appearing on other UIViewControllers. Here is the workaround solution:
func addFloatingButton() {
let keyWindow = UIApplication.shared.keyWindow
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = .red
keyWindow?.addSubview(button)
}

Link an #IBAction to a button in a .xib view from a ViewController

I have created a .xib file for a view that is going to be repetitive in my iOS app, inside of which there is a UIButton.
I have included that .xib view inside multiple UIViewControllers in my storyboard. I would like to link an #IBAction and an #IBOutlet to the button inside my .xib view that is specific to each UIViewController. In other words, I want every UIViewController to completely manage and handle the UIButton that is inside the .xib view.
Any idea if the above is feasible?
There are a couple of ways to do what you want.
The way I would do it is to give your custom view a closure that is run when the IBAction method is triggered. And each view controller that loads the view from the xib can pass in the closure to the view and the action will run when the button is clicked.
So here's the best solution I came up with so far.
Inside my .xib, I link the button to an #IBAction that is empty.
Again inside my .xib, I created a protocol with a single method that I will call inside the #IBAction created in step (1.)
The #IBAction will run the protocol method every time it is called, so every time the button is clicked.
Implement the protocol stub in every ViewController that needs to handle the #IBAction, and make sure to link that ViewController to the .xib using the protocol created in step (2.)
In your ViewController witch contains the xib view, just asign an action to the button inside xib view
class YourViewController:UIViewController{
override func viewDidLoad(){
//ListTitleView : a xib view, action button witch called theManageButton is inside it.
let theListTitleView = ListTitleView.init(frame:CGRect.init(x: 0, y: 0, width: self.view.frame.width, height: 100))
//Add action to theManageButton
theListTitleView.theManageButton.addTarget(self, action: #selector(yourFunction(Sender:)), for: .touchUpInside)
}
func yourFunction(Sender:UIButton){
//...Do something here
}
}

Why doesn't overriding a View Controller's navigationItem seem to do anything?

I've now looked at the TONS of posts that are about removing the text from the back button on a UINavigationBar. They don't seem to work anymore (iOS 10+). And even if they did, they don't "smell correct"...
My question is, why doesn't this work?
class MyViewController: UIViewController {
private var _navItem: UINavigationItem? = nil
override var navigationItem: UINavigationItem {
if _navItem == nil {
let item = super.navigationItem
let backBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
backBarButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .normal)
backBarButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .highlighted)
item.backBarButtonItem = backBarButtonItem
_navItem = item
}
return _navItem!
}
//...
}
I would expect to see a button that has clear text, but I see the title of the previous view controller as the back button text.
Do I need to subclass UINavigationController? or mess around with UINavigationBarDelegate?
This is an extremely common point of confusion, since the API is a bit counterintuitive on its face. The documentation that explains it is a little convoluted as well
When this navigation item is immediately below the top item in the stack, the navigation controller derives the back button for the navigation bar from this navigation item. When this property is nil, the navigation item uses the value in its title property to create an appropriate back button.
Basically, you need to do this on the "previous view controller" not the one that you see the back button on.
Though I'm not sure what your overall goals are, to do it "app wide", you have a couple options.
Put this into some sort of global base view controller class for your entire project. This is particularly cumbersome if you want to have different base classes for different parts of your apps, and of course when dealing with different types of system controllers.
UIAppearance methods, which have the downside (or upside?) of affecting every single bar button item, including the back button. This solution is generally too broad, though it is undeniably global.
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .normal)
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .highlighted)
Provide a custom left bar button item on the top view controller that you want the back button on. This solution again requires a base class of sorts, or good 'ol copy-pasta. And of course, loosing the ability to have the system back chevron provided for you. The alternative being an extension (Gabriel's answer provides a lovely example of that).
Everything has tradeoffs.
I had the exact same problem, wanted to use an icon without text, and the text kept showing up. After a long search and a lot of trying, I had to resort to creating a custom button and setting it as the leftBarButtonItem.
Here's the relevant code:
protocol CustomBackButton {
func configureBackButton()
func removeBackButton()
}
extension CustomBackButton where Self: UIViewController {
func configureBackButton() {
let backButton = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
backButton.setImage(#imageLiteral(resourceName: "backIconButton"), for: .normal)
backButton.addTarget(self, action: #selector(didTouchBackButton), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
self.navigationController?.navigationBar.tintColor = .lightGray
}
func removeBackButton() {
self.navigationItem.hidesBackButton = true
}
}
extension UIViewController {
func didTouchBackButton() {
self.navigationController?.popViewController(animated: true)
}
}
The removeBackButton() is only used because I have a popup window that shouldn't have a back button so I remove it.
Although Allen answer is correct that you need to do this on the "previous view controller" not the one that you see the back button on.
Although you can achieve your goal on same controller as well, here is my solution,
This function will return you UIBarButtonItem:
UIBarButtonItem *bbBack = [self overrideBackBarButtonItemWithTarget:self action:#selector(btnBackTapped:)];
Implement selector some where in your current controller:
- (IBAction)btnBackTapped:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
Function:
-(UIBarButtonItem*)overrideBackBarButtonItemWithTarget:(nullable id)target action:(nullable SEL)action {
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 65 , 44)];
[btn setImage:[UIImage imageNamed:#"nav-back"] forState:UIControlStateNormal];
btn.imageEdgeInsets = UIEdgeInsetsMake(0, -15, 0, 15);//move image to the right
[btn addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barbuttonBack = [[UIBarButtonItem alloc] initWithCustomView:btn];
UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer.width = -15;
UIViewController *_self = (UIViewController*)target;
[_self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects:spacer,barbuttonBack, nil] animated:NO];
return barbuttonBack;
}
Navigation back image
The credit should go to Allen Humphreys, but the question I posted above DOES make sure they would have no text on the back button, but you would have to ensure all your view controllers inherit from this class.
It didn't seem to do anything because I thought that setting the backButton text is for when the view controller itself is the one currently visible on the stack, but what it actually means is "what is the back button title when this view controller is second from the top?"
My question arose because the view controller 'second from the top' did not use this overridden navigationItem.

Custom back bar item button

Rather than going back 1 view controller, I am attempting to pop back to the first view controller under the navigation controller.
func setupBackBarButtons() {
let myBackButton = UIButton()
myBackButton.addTarget(self, action: #selector(presentListController), forControlEvents: .TouchUpInside)
let myCustomBackButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.backBarButtonItem = myCustomBackButtonItem // This works
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem // This doesn't.
}
func presentListController() {
navigationController?.popToViewController((self.navigationController?.viewControllers[0])!, animated: true)
}
I'm slightly confused to why I can add the custom button to the leftBarButtonItem but not the backBarButtonItem.
I only want to add a target to pop it back to the first view controller but not change the title (Still keep it as back with the < in the in corner). I would do this with the leftBarButtonItem but I'm not to sure how to keep the < in the title.
Do not change the functionality of the back button. When you try to do that, you just break the back button. You can customize the back button's appearance but you cannot change its action. You must provide a nil action and target so that the back button will still work as a back button.
To customize the back button's behavior, change the behavior of the navigation controller itself. Give the navigation controller a delegate and implement navigationController:didShowViewController:animated:. In your implementation, look to see how we got here. If we got here because we popped to it, pop further (to the root).

Ios - action on button in nib file

Good evening,
I'm wondering if displaying a nib file as a subview is the more standardized way of displaying a subview when compared to hiding and unhiding a view of the same class.
Also,
How would i be able to set an action on the buttons in the nib file?
let test = xWork.loadViewFromNib()
test.center = view.center
self.view.addSubview(test)
Currently doing the above. Even though my xWork nib has a class with an outlet for a button, i'm unsure as to how to set an action to it.
Thank you
Please clarify your first question. As for your second question, how to set the action of a button in a nib, try this:
In your nib, create a callback closure:
var onClickCallback: (Void -> Void)?
and in your button's IBAction, call the closure
#IBAction func buttonAction(sender: IBAction) {
onClickBallback?()
}
Then, where you use your nib, set the button callback:
let test = xWork.loadViewFromNib()
test.center = view.center
self.view.addSubview(test)
test.onClickCallback = {
print("Button clicked!")
// Try this to dismiss the view.
test.removeFromSuperview()
}
If your nib file owner is its super view(or view controller),just CTRL+DRAG an target-action.

Resources