Segue with no button help needed - ios

I have checked previous queries all but they are different or too advanced for me. I have buttons done programatically so I need to segue using program as well. I am not sending anything, just to go to the next ViewController. Documentation said if not passing anything just use: - (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination. But I don't understand plus (+) or class yet. Also I cannot just put gjRelaysViewController as destination, Xcode says it is not declared. It is correct for me to create an instance? I have tried prepare for segue but nothing came out of it. Thanks.
Source: gjViewControllers
#property (nonatomic, strong) UIStoryboardSegue *relaysSegue;
#property (nonatomic, strong) gjRelaysViewController *relays;
[_pushButton1 addTarget:self action:#selector(relayCircuitsOn:) forControlEvents:UIControlEventTouchDown];
-(void)relayCircuitsOn: (id) sender
{
_relaysSegue = [[UIStoryboardSegue alloc] initWithIdentifier:#"toRelays" source:self destination: _relays];
}
Destination: gjRelaysViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if(![segue.identifier isEqual: #"toRelays"])
{
return;
}
}

Ctrl-Drag in InterfaceBuilder from ViewController 1 to ViewController 2 to create a segue. Click on the segue and give it a name in the right pane.
In your code in relayCircuitsOn call
[self performSegueWithIdentifier:#"Insert the name you specified above" sender:self];
and you should be all set.
Alternatively you can do the transition in code:
Import ViewController 2 in ViewController1.m `#import "ViewController2.h"
Create a new instance of ViewController2: ViewController2 *vc = [[ViewController2 alloc] init];
Push it to the navigation stack (if you have one?): [self.navigationController pushViewController:vc animated:YES];

Related

Passing data from one ViewController to another when switching tabBarController index instead of segueing

My app contains a tabBarController with two tabs. Tab number 0 contains SearchViewController, tab number 1 contains MatchCenterViewController. What I want to do is set the value of MatchCenterViewControllers didAddNewItem property before switching to that tab. I know that if I had done this with a segue, it would be done as so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowMatchCenterSegue"]) {
MatchCenterViewController *controller = (MatchCenterViewController *) segue.destinationViewController;
self.didAddNewItem = 1;
controller.didAddNewItem = self.didAddNewItem;
}
}
However I don't want to do this with a segue, I want it to simply set didAddNewItem of MatchCenterViewController before calling [self.tabBarController setSelectedIndex:1];. Is it possible to do this?
You should be able to pull the view controller out of [self.tabBarController viewControllers]
A bit of pseudo-code for you to try and mess about with:
UIViewController *toViewController = [self.tabBarController viewControllers][1];
if ([toViewController isKindOfClass:[MatchCenterViewController class]]) { // This is just for the sake of safety. Good practice to get into type checking before you cast.
MatchCenterViewController *matchViewController = (MatchCenterViewController *)toViewController;
matchViewController.didAddNewItem = true; // Really this should be a Bool!
}
[self.tabBarController setSelectedIndex:1];
Best to use a protocol and delegate, this is the cleanest way of passing this data to another view controller. It also decouples your code to make it easier to manage later. For example you build an iPad version with no UITabbarController.
Define a protocol in SearchViewController.h:
//under the #import
#protocol SearchViewControllerDelegate
//define a property to send delegate messages to
#property (nonatomic,strong) id <SearchViewControllerDelegate> delegate;
//after #end
#protocol SearchViewControllerDelegate <NSObject>
- (void)searchViewControllerDidSearchWithResult:(NSArray *)result;
Then when you are setting up the view controllers assign MatchViewController to be the SearchViewControllers delegate.
//in SearchViewController.m send the delegate message
[self.delegate searchViewControllerDidSearchWithResult:arr];
Then in MatchViewController adopt the protocol to get messages.

Xcode - Using Delegate and Segue to Unlock button in first ViewController

I have this simple project where, when a button on a second ViewController (SecondViewController) is pressed a Delegate function send a string to the first ViewController and Unhide a new button which is not visible.
I have no problem now doing this however, if I add a third view between these to windows the function stops working.
Here what I mean:
In order to do this I use
In ViewController.h:
#interface ViewController : UIViewController <SecondViewControllerDelegate>
In ViewController.m I implemented a Segue function which detected an Identifier and send the value to second view :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"to2"]) {
SecondViewController *SecondView = segue.destinationViewController ;
SecondView.delegate = self;
}
}
-(void)done:(UIButton*)name{
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"back to first view, name=%# ", name);
level2But.hidden = NO;
}
To return to the first window i have created a returnButton in SecondViewController.h:
#protocol SecondViewControllerDelegate <NSObject>
-(void) done:(UIButton*)returnButton; //variable passed to ViewController
#end
#interface SecondViewController : UIViewController{
IBOutlet UIButton *returnButton;
id delegate; //declare delegate as an object
}
#property (nonatomic, strong) id <SecondViewControllerDelegate> delegate;
-(IBAction)returnButtonPressed:(id)sender;
In SeconViewController.m I have:
-(IBAction)returnButtonPressed:(id)sender{
[self.delegate done:returnButton];
}
This code is working just fine However if I had a third View It stops Working.
This is not working anymore.
AnySuggestion? Any Help?
This is the project file:
http://goo.gl/3rJOje
I did checked your code and I could see the self.delegate is nil and hence you are not able to move in SecondViewController.m.
-(IBAction)returnButtonPressed:(id)sender{
[self.delegate done:returnButton];
}
so make sure you pass the delegate.
Also if you want to know how to pass the data(delegate) then go through the below tutorial :
http://www.appcoda.com/storyboards-ios-tutorial-pass-data-between-view-controller-with-segue/

Get ViewController from Storyboard

I have two ViewControllers, which aren`t (directly) connected with a Segue. But I want to change a String in the second VC, which i get in the first one. My try was:
#import "secondViewController.h"
#interface firstViewController ()
#property NSString* originalString;
#end
#implementation firstViewController
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc = [secondViewController new];
svc.anotherString = self.originalString;
}
But it dosent work, because I've only created a instance of the second VC, so the value was not saved. Also I can`t use the Storyboard ID, because I use Xcode 5.
I have a menuVC from which you can get to the firstVC and the secondVC. And from the firstVC I can go back (with the navigationbackbarbutton) to the menu. so: menu->firstVC->menu. menu->secondVC->...->menu
My try with StoryboardID:
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
secondViewController* svc =[[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:#"secondVCSrorybradID"];
svc.anotherString = self.originalString;
}
you can pass the string to second view controller with this code.
secondViewController* svc =[self.storyboard instantiateViewControllerWithIdentifier:#"Your Second VC's Storyboad ID"];
svc.anotherString = self.originalString;
[self presentViewController:svc animated:YES completion:nil];
//you have to create anotherString property in second View Controller's .h File.
now you can get the string of originalString to second VC. Now you can get this value back second VC to first VC.
hope this helps you.
You should pass your data successively through your UIViewControllers navigation. If, for example, you have a navigation like FirstVC > SecondVC > ThirdVC :
In your FirstVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((SecondVCClass*) segue.destinationViewController).secondVCString = _firstVCString;
}
With secondVCString being a #property in your second ViewController.
In your SecondVC.m, use :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((ThirdVCClass*) segue.destinationViewController).thirdVCString = _secondVCString;
}
With of course thirdVCString being a #property in your third ViewController.
Edit:
As you updated your question, here is what I suggest :
In your MenuVC, add this :
#property (nonatomic, weak) NSString *importantString;
In your FirstVC and SecondVC, add this :
#property (nonatomic, weak) MenuVCClass *menu;
When you push to FirstVC or SecondVC, use prepareForSegue to set the destination's view controller menu property to your menu :
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"FirstSegue"])
((FirstVC*) segue.destinationViewController).menu = self;
else if ([segue.identifier isEqualToString:#"SecondSegue"])
((SecondVC*) segue.destinationViewController).menu = self;
}
In FirstVC or SecondVC, you can change the NSString value from your menu using _menu.importantString = #"";
You should never use new to create a view controller.
If you're using storyboards but not using segues, you can still create a view controller from the storyboard and invoke it.
Use the method instantiateViewControllerWithIdentifier: to create an instance of the target view controller. The set the properties you want to set, and finally make a call to display it (present it modally, push it onto the navigation stack, or whatever is appropriate for your program.)

How to add subview to another viewcontroller?

I'm doing an Ipad app. Now I have 2 viewcontrollers, ViewController has a button1 which has a popover segue to the second viewcontroller(PopoverController). Then, the PopoverController has a button2, if I click the button2, I'll receive some UIImage from my server. I want to add fews subviews of UIImageView to the ViewController to display these images if I click the button2.
The button1 works well, the PopoverController can pop up as expected. BUT when I click the button2, nothing happend. I want to know how can I pass the data between 2 viewcontrollers and how to add subviews to another one.
Some codes relating to my problem:
ViewController.h:
#import <UIKit/UIKit.h>
#class PopoverController;
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button1;
#property (strong, nonatomic) PopoverController *popoverController;
#end
PopoverController.h:
#import <UIKit/UIKit.h>
#class ViewController;
#interface PopoverController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button2;
#property (strong, nonatomic) UIImage *tempImg;
#property (strong, nonatomic) ViewController *viewController;
- (IBAction)addsubviews:(id)sender;
#end
I can not just use [viewController.view addSubview:img1]; in the - (IBAction)addsubviews:(id)sender;method to addsubview. So someone can help me? :)
====1st update====
Someone suggest that I have to use - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method. I have tried this one by control click the button2 and create a custom segue between button2 and ViewController. When I clicked the button2, it showed :
Terminating app due to uncaught exception 'NSGenericException', reason: 'Could not find a navigation controller for segue 'change'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
So I'm wondering whether I should add a NavigationController. If so, what should I do?
====2nd update====
I use Paramasivan 's code now, and I found the way to call method from another viewcontroller. The problem now is the newly added subview in my viewcontroller doesn't show up. I guess I have to update my viewcontroller in order to make it visible.
in my - (IBAction)addsubviews:(id)sender; method, i invoke the method in ViewController by [self.viewController createSubViewWithImage:_tempImg];
so the method can be invoked when i click the button2, but the view of viewcontroller has nothing changed.
Add this in - (void)viewDidLoad,
self.popoverController = [[PopoverController alloc] init];
self.popoverController.viewController = self;
Make sure that in no other places, you are setting self.popoverController = ....
Do NOT add anything like self.viewController = ... in popovercontroller class. And you dont have to do self.viewController.popoverController = self; as well. Just remove these lines if you already have it.
Once these are done, make sure that you are displaying self.popoverController only in the popover and you are not creating a new object for popoverController class there. So if these are fine, you can use any approach you want for passing the image from popoverController class to viewController class.
as you mentioned in your comment you can use [self.viewController createSubViewWithImage:_tempImg]; in your popovercontroller class.
Update:
If you are doing via storyboard, you need to set this in prepareForSegue method and you dont have to create self.popoverController at all. Remove that part in your case. You can follow the procedure mentioned here to set up a custom segue and implement prepareForSegue method to pass the object. Source: On storyboards, views and passing data along
Set the name of segue in storyboard to "CustomSegue"
Implement prepareForSegue method
Inside the method, check if name of segue matches "CustomSegue" and then set the viewController in the popoverController object there as,
Try,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"CustomSegue"]) {
PopoverController *popoverController = [segue destinationViewController];
popoverController.viewController = self;
}
}
After doing this, you need to call [self.viewController createSubViewWithImage:_tempImg]; in your popoverController class.
Check out the Communicating with Objects doc, there are several ways to do what you want.
In ViewController.h add the following
-(void)createSubViewWithImage:(UIImage *)imageDownloaded {
UIImageView *imageViewTemp = [[UIImageView alloc] initWithImage:imageDownloaded];
[self.view addSubView:imageViewTemp];
}
In PopoverController.h add the following
#property (nonatomic, retain) ViewController *viewControllerPassed;
And after image downloaded, call the following in PopoverController.h
[viewControllerPassed createSubViewWithImage:imageDownloaded];
You can pass data using the below method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ViewController2"])
{
ViewController2 *v2VC = [segue destinationViewController];
v2VC.yourData = self.someStuff;
}
}

How to Dismiss a Storyboard Popover

I've created a popover from a UIBarButtonItem using Xcode Storyboards (so there's no code) like this:
Presenting the popover works just fine. However, I can't get the popover to disappear when I tap the UIBarButtonItem that made it appear.
When the button is pressed (first time) the popover appears. When the button is pressed again (second time) the same popover appears on top of it, so now I have two popovers (or more if I continuer pressing the button). According to the iOS Human Interface Guidelines I need to make the popover appear on the first tap and disappear on the second:
Ensure that only one popover is visible onscreen at a time. You should not display more than one popover (or custom view designed to look and behave like a popover) at the same time. In particular, you should avoid displaying a cascade or hierarchy of popovers simultaneously, in which one popover emerges from another.
How can I dismiss the popover when the user taps the UIBarButtonItem for a second time?
EDIT: These problems appear to be fixed as of iOS 7.1 / Xcode 5.1.1. (Possibly earlier, as I haven't been able to test all versions. Definitely after iOS 7.0, since I tested that one.) When you create a popover segue from a UIBarButtonItem, the segue makes sure that tapping the popover again hides the popover rather than showing a duplicate. It works right for the new UIPresentationController-based popover segues that Xcode 6 creates for iOS 8, too.
Since my solution may be of historical interest to those still supporting earlier iOS versions, I've left it below.
If you store a reference to the segue's popover controller, dismissing it before setting it to a new value on repeat invocations of prepareForSegue:sender:, all you avoid is the problem of getting multiple stacking popovers on repeated presses of the button -- you still can't use the button to dismiss the popover as the HIG recommends (and as seen in Apple's apps, etc.)
You can take advantage of ARC zeroing weak references for a simple solution, though:
1: Segue from the button
As of iOS 5, you couldn't make this work with a segue from a UIBarButtonItem, but you can on iOS 6 and later. (On iOS 5, you'd have to segue from the view controller itself, then have the button's action call performSegueWithIdentifier: after checking for the popover.)
2: Use a reference to the popover in -shouldPerformSegue...
#interface ViewController
#property (weak) UIPopoverController *myPopover;
#end
#implementation ViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// if you have multiple segues, check segue.identifier
self.myPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if (self.myPopover) {
[self.myPopover dismissPopoverAnimated:YES];
return NO;
} else {
return YES;
}
}
#end
3: There's no step three!
The nice thing about using a zeroing weak reference here is that once the popover controller is dismissed -- whether programmatically in shouldPerformSegueWithIdentifier:, or automatically by the user tapping somewhere else outside the popover -- the ivar goes to nil again, so we're back to our initial state.
Without zeroing weak references, we'd have to also:
set myPopover = nil when dismissing it in shouldPerformSegueWithIdentifier:, and
set ourself as the popover controller's delegate in order to catch popoverControllerDidDismissPopover: and also set myPopover = nil there (so we catch when the popover is automatically dismissed).
I found the solution here https://stackoverflow.com/a/7938513/665396
In first prepareForSegue:sender: store in a ivar/property the pointer to the UIPopoverController and user that pointer to dismiss the popover in the subsequent invocations.
...
#property (nonatomic, weak) UIPopoverController* storePopover;
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue
sender:(id)sender {
if ([segue.identifier isEqualToString:#"My segue"]) {
// setup segue here
[self.storePopover dismissPopoverAnimated:YES];
self.storePopover = ((UIStoryboardPopoverSegue*)segue).popoverController;
...
}
I've used custom segue for this.
1
create custom segue to use in Storyboard:
#implementation CustomPopoverSegue
-(void)perform
{
// "onwer" of popover - it needs to use "strong" reference to retain UIPopoverReference
ToolbarSearchViewController *source = self.sourceViewController;
UIViewController *destination = self.destinationViewController;
// create UIPopoverController
UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:destination];
// source is delegate and owner of popover
popoverController.delegate = source;
popoverController.passthroughViews = [NSArray arrayWithObject:source.searchBar];
source.recentSearchesPopoverController = popoverController;
// present popover
[popoverController presentPopoverFromRect:source.searchBar.bounds
inView:source.searchBar
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
#end
2
in view controller that is source/input of segue e.g. start segue with action:
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
if(nil == self.recentSearchesPopoverController)
{
NSString *identifier = NSStringFromClass([CustomPopoverSegue class]);
[self performSegueWithIdentifier:identifier sender:self];
}
}
3
references are assigned by segue which creates UIPopoverController - when dismissing popover
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
if(self.recentSearchesPopoverController)
{
[self.recentSearchesPopoverController dismissPopoverAnimated:YES];
self.recentSearchesPopoverController = nil;
}
}
regards,
Peter
I solved it creating a custom ixPopoverBarButtonItem that either triggers the segue or dismisses the popover being shown.
What I do: I toggle the action & target of the button, so it either triggers the segue, or disposes the currently showing popover.
It took me a lot of googling for this solution, I don't want to take the credits for the idea of toggling the action. Putting the code into a custom button was my approach to keep the boilerplate code in my view to a minimum.
In the storyboard, I define the class of the BarButtonItem to my custom class:
Then I pass the popover created by the segue to my custom button implementation in the prepareForSegue:sender: method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"myPopoverSegue"]) {
UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue;
[(ixPopoverBarButtonItem *)sender showingPopover:popSegue.popoverController];
}
}
Btw... since I have more than one buttons triggering popovers, I still have to keep a reference of the currently displayed popover and dismiss it when I make the new one visible, but this was not your question...
Here is how I implemented my custom UIBarButtonItem:
...interface:
#interface ixPopoverBarButtonItem : UIBarButtonItem
- (void) showingPopover: (UIPopoverController *)popoverController;
#end
... and impl:
#import "ixPopoverBarButtonItem.h"
#interface ixPopoverBarButtonItem ()
#property (strong, nonatomic) UIPopoverController *popoverController;
#property (nonatomic) SEL tempAction;
#property (nonatomic,assign) id tempTarget;
- (void) dismissPopover;
#end
#implementation ixPopoverBarButtonItem
#synthesize popoverController = _popoverController;
#synthesize tempAction = _tempAction;
#synthesize tempTarget = _tempTarget;
-(void)showingPopover:(UIPopoverController *)popoverController {
self.popoverController = popoverController;
self.tempAction = self.action;
self.tempTarget = self.target;
self.action = #selector(dismissPopover);
self.target = self;
}
-(void)dismissPopover {
[self.popoverController dismissPopoverAnimated:YES];
self.action = self.tempAction;
self.target = self.tempTarget;
self.popoverController = nil;
self.tempAction = nil;
self.tempTarget = nil;
}
#end
ps: I am new to ARC, so I am not entirely sure if I am leaking here. Please tell me if I am...
I have solved this problem with no need to keep a copy of a UIPopoverController. Simply handle everything in storyboard (Toolbar, BarButtons. etc.), and
handle visibility of the popover by a boolean,
make sure there is a delegate, and it is set to self
Here is all the code:
ViewController.h
#interface ViewController : UIViewController <UIPopoverControllerDelegate>
#end
ViewController.m
#interface ViewController ()
#property BOOL isPopoverVisible;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.isPopoverVisible = NO;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// add validations here...
self.isPopoverVisible = YES;
[[(UIStoryboardPopoverSegue*)segue popoverController] setDelegate:self];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
return !self.isPopoverVisible;
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
self.isPopoverVisible = NO;
}
#end
I took rickster's answer and packaged it into a class derived from UIViewController. This solution does require the following:
iOS 6 (or later) with ARC
Derive your view controller from this class
make sure to call the "super" versions of prepareForSegue:sender and shouldPerformSegueWithIdentifier:sender if you are overriding those methods
Use a named popover segue
The nice thing about this is you don't have to do any "special" coding to support the proper handling of Popovers.
Interface:
#interface FLStoryboardViewController : UIViewController
{
__strong NSString *m_segueIdentifier;
__weak UIPopoverController *m_popoverController;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender;
#end
Implementation:
#implementation FLStoryboardViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] )
{
UIStoryboardPopoverSegue *popoverSegue = (id)segue;
if( m_popoverController == nil )
{
assert( popoverSegue.identifier.length > 0 ); // The Popover segue should be named for this to work fully
m_segueIdentifier = popoverSegue.identifier;
m_popoverController = popoverSegue.popoverController;
}
else
{
[m_popoverController dismissPopoverAnimated:YES];
m_segueIdentifier = nil;
m_popoverController = nil;
}
}
else
{
[super prepareForSegue:segue sender:sender];
}
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
// If this is an unnamed segue go ahead and allow it
if( identifier.length != 0 )
{
if( [identifier compare:m_segueIdentifier] == NSOrderedSame )
{
if( m_popoverController == NULL )
{
m_segueIdentifier = nil;
return YES;
}
else
{
[m_popoverController dismissPopoverAnimated:YES];
m_segueIdentifier = nil;
m_popoverController = nil;
return NO;
}
}
}
return [super shouldPerformSegueWithIdentifier:identifier sender:sender];
}
#end
Source available on GitHub

Resources