I have an iPad app that has an UIPopOverController that is called from a seque. When the button is pushed the popover is presented. The user selects one of the items from the popover and the popover is dismissed. However, when the user pushes the button a second time, the app crashes. The weird part is that it works fine in the simulator but running it on a iPad device it crashes. I receive the following error: EXC_BAD_ACCESS. The following is my code:
#interface ViewController : UITableViewController<KeypadDelegate,PickerDelegate,UIPopoverControllerDelegate,UIPickerViewDataSource,UIPickerViewDelegate,UITextFieldDelegate>
#property (strong,nonatomic) UIPopoverController *myPopOver;
#end
#implementation ViewController
//...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"popOverSearchCustomerSegue"])
{
if([segue isKindOfClass:[UIStoryboardPopoverSegue class]])
{
[self.myPopOver dismissPopoverAnimated:YES];
self.myPopOver = [(UIStoryboardPopoverSegue *)segue popoverController];
}
UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
self.myPopOver = popoverSegue.popoverController; // this is where it errors out on the second time the button is pushed.
[segue.destinationViewController setDelegate:self];
}
else if ([segue.identifier isEqualToString:#"pushProposalSegue"])
{
ProposalViewController *propViewController = segue.destinationViewController;
propViewController.customerID = cust.ID;
propViewController.customerTitle = self.title;
}
}
//This method is called by the popover controller when the user selects a row
-(void) myCallBack:(SearchCustomer *)ff
{
custID = ff.ID;
[self.myPopOver dismissPopoverAnimated:YES];
cust = [custDOA getRecords:custID];
[self setFields];
[self setButtons:YES];
}
This is my interface for my popover that has a searchBar controller:
#interface CustomerSearchViewController : UITableViewController<UISearchBarDelegate>
#property (strong,nonatomic) NSMutableArray *customerSearchList;
#property (weak, nonatomic) ViewController *delegate;
#property (strong,nonatomic) NSMutableArray *resultsCustomerSearchList;
#property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
#end
Related
I have two viewController and iam using a delegate method to update a label in the first view controller with text entered in the second viewcontroller.
My problem is the value entered in the textfield(secondViewController) is not getting pushed into the UILabel(FirstVIewController)
My First ViewController.h
#import "SecondViewController.h"
#interface ViewController : UIViewController <CustomDelegate>
#end
First ViewController.m
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *infoLabel;
#end
#implementation ViewController
-(void)sendinfo:(NSString *)info
{
_infoLabel.text = info;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"info"])
{
SecondViewController *obj = segue.destinationViewController;
obj.userDelegate = self;
}
}
SecondViewController.h
#protocol CustomDelegate <NSObject>
-(void)sendinfo:(NSString *)info;
#end
#interface SecondViewController : UIViewController
#property (weak, nonatomic) id <CustomDelegate> userDelegate;
#property (weak, nonatomic) IBOutlet UITextField *textField;
SecondViewController.m
- (IBAction)sendInfo:(id)sender {
[self.userDelegate sendinfo:_textField.text];
UIViewController * controller = [self.storyboard instantiateViewControllerWithIdentifier:#"Home"];
[self presentViewController:controller animated:YES completion:Nil];
}
You are creating another UIViewController in your SecondViewController and the delegate is set to the viewController from where you are coming.
To see your changes, you should do a [self dismissViewControllerAnimated:YES completion:nil]; in your SecondViewController
Example:
- (IBAction)sendInfo:(id)sender {
[self.userDelegate sendinfo:_textField.text];
[self dismissViewControllerAnimated:YES completion:nil];
}
I had a UIViewController and a UIContainer View.
I want to simulate the UIAlertView effect, But I don't know how to build the Container View show and hide about the great method.
I had put the UITableView in my UIContainer View, I use delegate to send selected item to the UIViewController(ParentViewController) and show in the label.( segue name with alertSegue)
There have a show button in the ParentViewController,and I need click the selected item ,it will close(hide/dismiss?) the UIContainer View.
Now UIContainer View default is hidden,and storybaord screen shot like below:
My ParentViewController.h
#interface ViewController : UIViewController<ContainerViewBLETableDelegate>
#property (weak, nonatomic) IBOutlet UIButton *btn;
- (IBAction)btnAction:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *lb;
#property (weak, nonatomic) IBOutlet UIView *containerView;
-(IBAction)unwindSegue:(UIStoryboardSegue *)segue;
#end
.m file:
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)btnAction:(id)sender {
_containerView.hidden = NO;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue.identifier isEqualToString:#"alertSegue"])
{
ContainerViewBLETable *vc = segue.destinationViewController;
vc.delegate = self;
}
}
-(void) ContainerViewBLETable:(ContainerViewBLETable *)vc andSelectedDone:(NSString *)selectedStr
{
self.lb.text = selectedStr;
vc.view.hidden = YES;
}
-(IBAction)unwindSegue:(UIStoryboardSegue *)segue
{
}
Container ViewController .h :
#class ContainerViewBLETable;
#protocol ContainerViewBLETableDelegate <NSObject>
-(void) ContainerViewBLETable:(ContainerViewBLETable*)vc andSelectedDone:(NSString*)selectedStr;
#end
#interface ContainerViewBLETable : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property(nonatomic,assign) id<ContainerViewBLETableDelegate>delegate;
#property (weak, nonatomic) IBOutlet UITableView *tableVW;
#end
UIContainerView .m part:
.....
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate ContainerViewBLETable:self andSelectedDone:[NSString stringWithFormat:#"%ld",indexPath.row]];
}
.....
Although this code can pass the value to the UIViewController(ParentViewController),and the UIContainer View was hidden,but when I click the show button again , it was not any effect(I feel the container view was overlap lead to touch lose efficacy).
( by the way, I had try
[self.parentViewController dismissViewControllerAnimated:YES completion:nil];
it's not effect )
Question:
How to hide the container view use the delegate and that can touch the show button show container again?
And What great method to control the UIContainer view show and disappear in ParentViewController and UIContainerView ?
I really thank everyone and you can help me and better than better.
containerView and show button are in different viewControllers. So you can't just hide it. The simple way to achieve it is when you select the cell in containerView, present the ParentViewController, and the view in ContainerViewController will dismiss automatically.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate ContainerViewBLETable:self andSelectedDone:[NSString stringWithFormat:#"%ld",indexPath.row]];
ParentViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"ParentViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
To show the container, you can just dismiss the ParentViewController that presenting.
1.
- (IBAction)btnAction:(id)sender
{
[self.presentingViewController dismissModalViewControllerAnimated:YES];
}
Or
2. Set up segue to dismiss viewController and use prepareForSegue for delegate to communicate with other viewController.
Also REMOVE vc.view.hidden = YES in delegate you had implemented
-(void) ContainerViewBLETable:(ContainerViewBLETable *)vc andSelectedDone:(NSString *)selectedStr.
I tried many custom delegate examples in my code to send data from view B back to parent view A. Why am I getting NULL? There should be something I am missing. Please help. How can I call setupDate to receive (NSDate *)setAlarmDate in Parent view?
Parent view
#import "SetupViewController.h"
#interface ViewController : UIViewController <SetupViewControllerDelegate> {
NSTimer *timer;
}
#end
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[segue identifier] isEqualToString:#"setupview"]) {
NSLog(#"prepare for segue");
SetupViewController *svc = [[SetupViewController alloc] init];
svc.delegate = self;
-(void)setupDate:(NSDate *)setAlarmDate{
NSLog(#"Hey Hey");
NSLog(#"%#", setAlarmDate);
}
#end
View B
#protocol SetupViewControllerDelegate <NSObject>
#required
-(void)setupDate:(NSDate *)setAlarmDate;
#end
#interface SetupViewController : UIViewController
#property (nonatomic, strong) id <SetupViewControllerDelegate> delegate;
//IB
#property (weak, nonatomic) IBOutlet UIDatePicker *pDatePicker;
#end
.m
#import "SetupViewController.h"
#import "ViewController.h"
#implementation SetupViewController
-(void)viewWillDisappear:(BOOL)animated{
NSDate *date = [self.pDatePicker date];
[[self delegate] setupDate:date];
}
#end
You should access the UIViewController using the segue's destinationViewController property instead of allocating an instance of your own, which is why your alarmDate = NULL.
E.g.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"setupview"])
{
NSLog(#"prepare for segue");
SetupViewController *svc = [segue destinationViewController];
svc.delegate = self;
}
}
Also, as #Neru mentioned your delegate should be qualified as weak and not strong. The reason for this is because you will cause a retain cycle if you do not explicitly set the delegate = nil in the dealloc method.
I have a ViewController1 with a UILabel that can present a ViewController2 with a Modal Segue. I have a UITextField in ViewController2 that I need to access from ViewController1 so I can set my UILabel with the collected text.
I've tried working with the prepareForSegue without success. What should I do?
EDIT:
I'm using a Delegate, but I'm doing something wrong. Here's the code I'm using in ViewController2.h:
#class ViewController2;
#protocol VCProtocol
-(void)setName:(NSString *)name;
#end
#interface ViewController2 : UIViewController
#property (nonatomic, weak) id<VCProtocol> delegate;
#property (strong, nonatomic) IBOutlet UITextField *nameField;
- (IBAction)setButton:(id)sender
#end
ViewController2.m
-(IBAction)setButton:(id)sender
{
[self.delegate setName:nameField.text];
}
I conform to VCProtocol in my ViewController1.h. Then, in my ViewController1.m, I have this code:
- (void)setName:(NSString *)name
{
self.firstSignatureNameLabel.text = name;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqual:#"Sign"])
{
ViewController2 *VC = [segue destinationViewController];
VC.delegate = self;
}
}
You can create a protocol and set VC1 as delegate of VC2, and using prepareForSegue to set VC1 as VC2's delegate should work. I know that you said it didn't work, but I can't see why. Have a try with this :
Give an identifier to your segue (on storyboard), and implement prepareForSegue as shown below :
VC2Delegate.h :
#protocol VC2Delegate
- (void)updateLabel:(NSString *)text;
#end
VC1.h :
#import "VC2Delegate.h"
#interface VC1 : UIViewController <VC2Delegate>
// All your stuff
#end
VC1.m :
- (void)updateLabel:(NSString *)text {
[_label setText:text];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"YourSegueIdentifier"]) {
VC2 * vc = [segue destinationViewController];
[vc2 setDelegate:self];
}
}
VC2.h :
#import "VC2Delegate.h"
#interface VC2 : UIViewController
#property (weak, nonatomic) id<VC2Delegate>delegate;
#end
VC2.m
- (void)textWasUpdated { // or whatever method where you detect the text has been changed
if (_delegate)
[_delegate updateLabel:[_textView text]];
}
Tell me if it works. Else, is prepareForSegue even been called ?
EDIT : Updated my answer (wasn't exactly what you needed).
But as you say it doesn't work :
Is prepareForSegue called ?
If so, is the delegate method called ?
If the delegate method isn't called, check that delegate is not nil.
You might want to remove the segue, and present it modally by yourself, using presentViewController:animated:completion:, like that :
- (IBAction)buttonWasTapped {
static NSString * const idModalView = #"modalView";
static NSString * const storyBoardName = #"MainStoryBoard"
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyBoardName bundle:nil];
VC2 * vc = [storyboard instantiateViewControllerWithIdentifier:idModalView];
[vc setDelegate:self];
[self.navigationController presentViewController:vc2 animated:YES completion:nil];
}
When I ran into this problem, I chose the singleton approach and it works.
I have two view controllers. MainViewController pushes SignUp ViewController. Once SignUp View Controller dismisses, I need access to the same instance of MainViewController to update a UIButton.title.
.h MainView
//MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UITableViewController
-(void)loggedIn;
#end
.m
#interface MainViewController ()
#end
-(void)loggedIn
{
NSLog (#"This is Logged in inside MainView.m");
self.logInOutButton.title = #"Logout";
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
Signup *signUp = [destinationViewController isKindOfClass:[SignUp class]] ? (Signup*)destinationViewController : nil;
signUp.mainViewController = self;
NSLog(#"Preapre For Segue %#", self);
}
.h SignUp
#import <UIKit/UIKit.h>
#import "MainViewController.h"
#interface SignUp : UIViewController
#property (strong, nonatomic) MainViewController *mainViewController;
#end
.m
#synthesize mainViewController;
- (void) loggedIn
{
NSLog(#" MainViewController %#", mainViewController);
[mainViewController loggedIn];
self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
When I do NSLog I'm getting different values:
Preapre For Seg <STMainViewController: 0x9f7d600>
MainViewController (null)
Create a protocol / delegate. There are numerous resources on how to achieve this. In summary, you create the protocol on the destination viewController with the method you wish to run on the source and then you subscribe to that delegate from the source viewController.
Protocol Delegate
Dont forget to set the delegate from your prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
Signup *signUp = segue.destinationViewController;
signup.delegate = self;
NSLog(#"Preapre For Segue %#", self);
}