I have four buttons and one segue. When any of these buttons get touch up inside event, it will load respective images on the destination view controller. For that, I am recognizing buttons through their tag property [each button (0,1,2,3)]. Here is my code:
- (IBAction)Session1Btn:(id)sender {
[self performSegueWithIdentifier:#"isSection" sender:self];
}
- (IBAction)Session2Btn:(id)sender {
[self performSegueWithIdentifier:#"isSection" sender:self];
}
- (IBAction)Session3Btn:(id)sender {
[self performSegueWithIdentifier:#"isSection" sender:self];
}
- (IBAction)Session4Btn:(id)sender {
[self performSegueWithIdentifier:#"isSection" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SectionViewController *sVC=[segue destinationViewController];
if([segue.identifier isEqualToString:#"isSection"])
{
// the following line gives an error!
NSInteger tagIndex = [(UIButton *)sender tag];
[sVC setSelectedButton:[NSNumber numberWithInteger:tagIndex]];
}
}
However I am getting the following error:
You must have written UIButton UITouchUpInside event like this:
- (IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"isSection" sender:sender];
}
Same way for all your buttons.
Now add one variable UIButton *selectedButton in .h file. and update your button event methods like below:
- (IBAction)buttonPressed:(id)sender
{
selectedButton = (UIButton *)sender;
[self performSegueWithIdentifier:#"isSection" sender:sender];
}
And now you have selectedButton reference, which can be used in other methods. So that,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UIButton *)sender {
SectionViewController *sVC=[segue destinationViewController];
if([segue.identifier isEqualToString:#"isSection"])
{
[sVC setSelectedButton:[NSNumber numberWithInteger: selectedButton.tag]];
}
}
Swift
Create property for the selected button:
var selectedButton: Int = Int()
In each IBAction assign selectedButton to either 1 or 2 and add performSegue(withIdentifier: "yourSegueIdentifier", sender: self).
In prepare for segue add the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if buttonTapped == 1 {
// what ever happens when first button selected
} else if buttonTapped == 2 {
// what ever happens when second button selected
}
}
Related
I am fresh in iOS and objective-c. I am learning how to use segues, especially unwind segue.
while reading, I got a bit confused about the usage of 'shouldPerformSegueForIdentifier' and 'performSegueForIdentifier'.
I created an example contains two 'ViewControllers', 'ViewController.m' as shown in the code posted below 'VC_1' and 'ServiceViewController'
my questions are:
-when and how should I use 'performSegueForIdentifier'
-when and how should I use 'shouldIPerformSegueForIdentifier'?
VC_1:
#import "ViewController.h"
#import "ServiceViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a
nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)btnStartService:(UIButton *)sender {
if (sender.tag == 1) {
NSLog(#"CLICKED");
[self performSegueWithIdentifier:#"seguePassInterval" sender:(id)
sender];
}
}
-(IBAction)btnExitApp:(UIButton *)sender {
NSLog(#"EXIT_CLICKED");
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"seguePassInterval"]) {
((ServiceViewController*)segue.destinationViewController).data = #"testData"; //passing data to destinationViewController of type "TestViewController"
NSLog(#"SEGUE");
}
}
#end
img
The prepareForSegue method is called right before the segue is executed, and allow to pass data between ViewController among other things, you can by example check if the identifier of your segue is "XxX" and pass some data or if is "YYY" call for a method
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"seguePassInterval"]) {
((TestViewController*)segue.destinationViewController).data = #"testData"; //passing data to destinationViewController of type "TestViewController"
NSLog(#"SEGUE");
}
}
method performSegueWithIdentifier is used to as his name says execute a segue using his identifier, you can perform a segue when you need it
and finally shouldPerformSegue is used to avoid perform a segue if your app is in some state, for example if you don't have the destinationViewController data yet you can return false until you get that
Hope this helps
I have passed button tag to another Viewcontroller.
It's passed but when I'm calling any another method like play audio play on that selected button, player not working...
I have tried below code :
It passes id of button clicked to the other view. Where I build player and and based on button id I want to play a poem on other view and control functions like volume control, progressbar, duration, play, push, next, etc...
While I'm giving a method to a particular button id to play poem it is not working...
This below code for play a poem in another view where I build player.
`
- (IBAction)btnpoemclicked:(id)sender {
btnPressed = [NSString stringWithFormat:#"%li", (long)[sender tag]];
NSLog(#"selected poem -%#",btnPressed);
PlayerController *pl=[[PlayerController alloc] init];
pl.btnPressed=btnPressed;
[pl setBtnPressed:pl.btnPressed];
[self performSegueWithIdentifier:#"player" sender:sender];
}
You should pass values in prepareForSegue when using Segues.
Update your code like
- (IBAction)btnPoemClicked:(id)sender {
btnPressed = [NSString stringWithFormat:#"%li", (long)[sender tag]];
[self performSegueWithIdentifier:#"player" sender:self];
}
And then In prepareForSegue pass the values to destinationViewController like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"player"]) {
PlayerController *playerController = (PlayerController *)segue.destinationViewController;
playerController.btnPressed = btnPressed;
}
}
Implement your button action like,
- (IBAction)buttonPoemClicked:(id)sender {
[self performSegueWithIdentifier:#"Player" sender:sender];
}
Pass your value by implementing prepareForSegue,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Player"]) {
PlayerController *playerController = (PlayerController *)segue.destinationViewController;
playerController.pressedButtonTag = [NSString stringWithFormat:#"%li", (long)[sender tag]];
}
}
Additional Note : Try to make your variable/class names little more understandable or try to follow apple recommended naming conventions.
In -prepareForSegue:sender:, how do I check if the sender was the view controller in which the -prepareForSegue:sender: is written?
In other words, how do I compare the sender argument to see what was passed when calling -performSegueWithIdentifier:sender:
I tried:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"identifier"])
if ([(MyViewControllerClass *)sender isEqual:self]) {
// stuff
} else {...}
}
}
Also tried == in place of isEqual:.
The else part is always executed.
you can try this
if([sender isKindOfClass:[MyViewControllerClass class]){
//do stuff
}
Update: First below code was crap ofcourse, here the version that does make sense:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self performSegueWithIdentifier:#"segueIdentifier" sender:self];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (segue.sourceViewController == sender) {
NSLog(#"i have been the sender");
}
}
Stupid last version as reference what you should not do - answer to fast:
I think you don`t want to check the sender. The sender could also be a Button activating the segue - i guess that you want to check against the sourceViewController.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (segue.sourceViewController == self) {
NSLog(#"i have been the source");
}
}
This will check if the segue will transition from the current ViewController.
I've built a custom UITabBarController with Storyboards/Segues and UIViewController containment. Here is a link to it: https://github.com/mhaddl/MHCustomTabBarController
The UIViewControllers which will be presented by the Container are stored in a NSMutableDictionary (keys are the segues' identifiers). Everything is working fine until the point is reached where I come back to a earlier presented ViewController. At this moment "dealloc" gets called on this ViewController before it is presented.
How can I prevent "dealloc" from getting called so it can be used to unsubscribe from Notifications, and nil delegates.
MHCustomTabBarController:
#implementation MHCustomTabBarController {
NSMutableDictionary *_viewControllersByIdentifier;
}
- (void)viewDidLoad {
[super viewDidLoad];
_viewControllersByIdentifier = [NSMutableDictionary dictionary];
}
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.childViewControllers.count < 1) {
[self performSegueWithIdentifier:#"viewController1" sender:[self.buttons objectAtIndex:0]];
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
self.destinationViewController.view.frame = self.container.bounds;
}
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (![segue isKindOfClass:[MHTabBarSegue class]]) {
[super prepareForSegue:segue sender:sender];
return;
}
self.oldViewController = self.destinationViewController;
//if view controller isn't already contained in the viewControllers-Dictionary
if (![_viewControllersByIdentifier objectForKey:segue.identifier]) {
[_viewControllersByIdentifier setObject:segue.destinationViewController forKey:segue.identifier];
}
for (UIButton *aButton in self.buttons) {
[aButton setSelected:NO];
}
UIButton *button = (UIButton *)sender;
[button setSelected:YES];
self.destinationIdentifier = segue.identifier;
self.destinationViewController = [_viewControllersByIdentifier objectForKey:self.destinationIdentifier];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([self.destinationIdentifier isEqual:identifier]) {
//Dont perform segue, if visible ViewController is already the destination ViewController
return NO;
}
return YES;
}
#pragma mark - Memory Warning
- (void)didReceiveMemoryWarning {
[[_viewControllersByIdentifier allKeys] enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
if (![self.destinationIdentifier isEqualToString:key]) {
[_viewControllersByIdentifier removeObjectForKey:key];
}
}];
}
#end
MHTabBarSegue:
#implementation MHTabBarSegue
- (void) perform {
MHCustomTabBarController *tabBarViewController = (MHCustomTabBarController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) tabBarViewController.destinationViewController;
//remove old viewController
if (tabBarViewController.oldViewController) {
[tabBarViewController.oldViewController willMoveToParentViewController:nil];
[tabBarViewController.oldViewController.view removeFromSuperview];
[tabBarViewController.oldViewController removeFromParentViewController];
}
destinationViewController.view.frame = tabBarViewController.container.bounds;
[tabBarViewController addChildViewController:destinationViewController];
[tabBarViewController.container addSubview:destinationViewController.view];
[destinationViewController didMoveToParentViewController:tabBarViewController];
}
#end
"At this moment "dealloc" gets called on this ViewController before it is presented." -- no, not really. Dealloc is being called on a controller that never gets on screen, not the one you came from initially or are going back to. The way your segue is set up, and the fact that you keep a reference to your controllers in the dictionary, means that they never get deallocated. Segues (other than unwinds) ALWAYS instantiate new view controllers, so what's happening is that a new instance of, say VC1 is created when you click on the first tab (and a segue is triggered), but you never do anything with that controller (which would be self.destinationViewController in the custom segue class) so it's deallocated as soon as the perform method exits.
Depending on where you setup any delegates or notification observers, this might not be a problem -- this controller that's created, and then immediately deallocated never has its viewDidLoad method called, so if you do those things in viewDidLoad, they won't ever happen for this transient view controller.
If you don't want this to happen, then you need to make your transitions in code without using segues.
Sorry if the title isn't very clear, but hopefully I can elaborate here.
I have a ViewController MatchLineupViewController, which displays 22 buttons to represent rugby players on a team. When the user taps any of these buttons, a modal segue is called programmatically in the following method:
- (IBAction) showSquadSelector:(UIButton *)sender {
[self performSegueWithIdentifier:#"SeguePopupSquad" sender:sender];
}
The modal ViewController which is then displayed is called SquadSelectViewController. It passes back a selected player object to the MatchLineupViewController, which is acting as a delegate. This works perfectly.
However, I want to assign the profile_picture attribute of the returned object to the UIButton that sent the segue in the first place.
EDIT - The returned object is an NSDictionary as shown in the following code:
- (void) selectPlayer:(NSDictionary *)player forButton:(UIButton *)sender {
[sender.imageView setImage:[UIImage imageWithContentsOfFile:[player objectForKey:#"profile_picture"]]];
}
How would I go about doing this? If you require any further code to understand what I am asking, I can provide it.
Many thanks,
Chris
EDIT -
- (IBAction) showSquadSelector:(UIButton *)sender {
[self performSegueWithIdentifier:#"SeguePopupSquad" sender:sender];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"SeguePopupSquad"]) {
SquadSelectViewController *ssvc = (SquadSelectViewController *) segue.destinationViewController;
ssvc.myDelegate = self;
ssvc.senderButton = sender;
}
}
- (void) selectPlayer:(NSDictionary *)player forButton:(UIButton *)sender {
[sender.imageView setImage:[UIImage imageWithContentsOfFile:[player objectForKey:#"profile_picture"]]];
NSLog(#"%#", [player description]);
NSLog(#"%#", [sender description]);
}
You can forward the sender of your showSquadSelector: method to the segue, like this:
[self performSegueWithIdentifier:#"SeguePopupSquad" sender:sender];
The sender of the segue would be the button that triggered the segue, so the code triggered from the segue would know what button has triggered it: your prepareForSegue: would have the correct UIButton. You can now add it to the returned dictionary at a predetermined key (say, #"senderButton") and examine it upon the return from the segue.