I have an InterfaceController with a table view. I created a segue from a cell of the tableview to another InterfaceController. If the segue type is "Push" contextForSegueWithIdentifier gets called. If the type is "Modal" contextForSegueWithIdentifier does not get called.
Never mind. According to the documentation contextForSegueWithIdentifier only gets called on a Push Segue. I needed to use the
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
method of the tableview to call
-(void)presentControllerWithNames:(NSArray *)names contexts:(NSArray *)contexts
When using a table view, use this segue method to pass an object to your segue.
- (id)contextForSegueWithIdentifier:(NSString *)segueIdentifier inTable:(WKInterfaceTable *)table rowIndex:(NSInteger)rowIndex {
return yourObject;
}
Related
I have a UIBarButtonItem on my UINavigationBar named selectRoleButton. Within this class (AEFeedsViewController) I have a protocol:
#protocol AERoleSelectProtocol <NSObject>
- (void)presentLoginViewController;
#end
The selectRoleButton is hooked up to AERoleSelectViewController by a target action IBAction, which holds a container that contains AERoleSelectTableViewController.
Here is a picture of my Storyboard
Below is the IBAction for selectRoleButton:
- (IBAction)selectRole:(UIBarButtonItem *)sender
{
NSLog(#"Sender: %#", sender); // Unable to breakpoint, NSLog won't log...?
AERoleSelectTableViewController *roleViewControler = [self.storyboard instantiateViewControllerWithIdentifier:#"RoleTableVC"];
roleViewControler.delegate = self; // delegate is not being passed/set
[self presentViewController:roleViewControler animated:YES completion:nil];
}
So when tapping the button, it presents AERoleSelectViewController, with AERoleSelectTableViewController in a container. AERoleSelectTableViewController has a delegate property of
AERoleSelectTableViewController.h
#property (weak, nonatomic) id <AERoleSelectProtocol> delegate;
The IBAction should be passing and setting the delegate object, but it is nil. Breakpoints do not seem to work, it won't break at all on the method. I've even tried writing my own method and wiring the button to that, but the breakpoint still won't catch it. I also cannot print anything using NSLog method for some reason.
In AERoleSelectTableViewController.m, my didSelectRowAtIndexPath: has a switch statement, one case uses the delegate and calls a method on AEFeedsViewController which conforms to the AERoleSelectProtocol.
AERoleSelectTableViewController.m
...
if (self.delegate) {
[self.delegate presentLoginViewController];
}
But since the delegate is nil, it obviously never gets called.
Edit:
I have tried disabling the target action IBAction on the selectRole button and using a segue to present it modally.
My segue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showRoleView"])
{
NSLog(#"Sender: %#", sender);
AERoleSelectTableViewController *roleViewController = (AERoleSelectTableViewController *)[segue destinationViewController];
roleViewController.delegate = self;
}
}
The method is being called, I can breakpoint and it is crashing on
roleViewController.delegate = self;
With the crash log:
[AERoleSelectViewController setDelegate:]: unrecognized selector sent to instance
As mentioned above, I have a delegate property on AERoleSelectTableViewController.h so it should be passing no problem. But it crashes instantly, viewDidLoad doesn't even get called on AERoleSelectTableViewController.
Is this something to do with how I'm setting it on a class within a container? I tried setting the delegate on the container class (AERoleSelectTableViewController) on [segue destinationViewController] and when viewDidLoad gets called, the delegate object is set, but only the container view is displayed (the just the tableView, no background etc, RoleSelectViewController doesn't present at all, which is expected given this code.
When setting the[segue destinationViewController] to the class which holds the container (RoleSelectViewController), the containers' class viewDidLoad is called first. So the delegate is not set. Then viewDidLoad gets called on the class holding the container, with the delegate set successfully. Am I able to pass the delegate from the class which holds the container, to the container even though the container gets called first? Is using a container even the correct approach? Is a container needed or can I just put a tableView straight in there?
I have a button in a cell which calls a protocol that has data that needs to be passed to the view controller by the segue. The segue is happening through storyboard. My current code uses the shouldperformsegue to return no when the button is pressed as the first segue that happens does not have the data.
Im guessing the segue and the protocol are being handled asynchronously.
But before I return NO I tell it to perform the segue at a delay. This delayed segue does have the data and works fine.
My question is there a way to wait for the protocol to finish and then perform the segue? Maybe with an execution block?
The other responders have hinted about this, but haven't stated it explicitly, so here goes.
Do not tie a segue directly to the button. Instead, control-drag from the source view controller SCENE to the destination scene to create a segue at the view controller level. Give the segue a unique identifier.
Then, in your button IBAction code, do the async network download. You may want to display a "loading, please wait" message or something while the download is taking place. Most async network calls take a completion block. In that completion block, wrap a call to performSegueWithIdentifier in a call to dispatch_async(dispatch_get_main_queue() so the segue gets invoked on the main thread. (#SantaClaus's answer shows the syntax for that.)
So your button IBAction code might look like this:
- (IBAction) buttonAction: (UIButton *) sender;
{
//Display a "please wait"message to the user if desired
doAsyncCallTakingBlock( completion: ^(NSData *data)
{
//parse the data, (or whatever)
dispatch_async(dispatch_get_main_queue(), ^
{
//This call uses the button as the sender. That might be appropriate,
//or not.
[self performSegueWithIdentifier:#"jumpToOtherViewController"
sender: sender];
});
}
}
With this approach the segue doesn't get called until the async method (Which I called doAsyncCallTakingBlock in my example code) has finished it's work. You might call an Alamofire request method, use an NSURLSession, or any other async method that takes a completion block.
Yes, I would use blocks. For example:
[Api getDataWithCompletion:^(BOOL success, NSString *data) {
if (success) {
self.data = data;
[self performSegueWithIdentifier:#"MySegue" sender:self];
} else {
NSLog(#"Failed to get data");
}
}];
Then, to pass it to the next view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"MySegue"]) {
TargetViewController *targetVC = (TargetViewController*)segue.destinationViewController;
targetVC.data = self.data;
}
}
Check out performSegueWithIdentifier on UIViewController. If you set up a segue between the view controllers in your storyboard (not from the button) and give it an identifier, you can perform the segue as soon as the data is ready.
Since you mentioned that your data is being fetch asynchronously, you might need to dispatch the performSegueWithIdentifier call to the main thread like so:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"jumpToOtherViewController" sender:self];
});
To actually pass the data on to the next view controller, you can use prepareForSegue as described here.
I am attempting to perform a segue after a done button is pressed from a custom signature alertView. The segue works perfect if I call it from a button inside the same view, but when I call from my signature class, I get a crash that says:
`'Receiver (<SuperSigApproveTableViewController: 0x79eea280>) has no segue with identifier 'approveSwap''
SignatureAlertViewController is the name of the class that calls the done method
- (IBAction)dismissAlertView:(id)sender {
[linerSig sendSigImageToServer];
[self.presentingViewController dismissViewControllerAnimated:YES completion:^{[sp sendToPDF];}];
}
inside the completion block is the method I am trying to call which is located inside of SuperSigApproveTableViewController
-(void)sendToPDF {
NSLog(#"sendtopdf");
[self performSegueWithIdentifier: #"approveSwap" sender: self];
}
I have alloc and init the SuperSigApproveTableView in the viewDidLoad method of SignatureAlertViewController. I am not sure why it is crashing does anyone have any clues?
I got a problem.
That when I initial a Controller I used initwithnib:bundle: method and then I think this controller has been loaded to the memory. So I call its method selectUnreadMessage to change its header. But I found that viewdidload was called after selectUnreadMessage.
Some one can tell me why ? Thanks.
- (void)selectUnreadSegmentedHeaderButton {
if ([YDNetworkingManager sharedNetworkingManager].badgeNumbers.count) {
for (NSNumber *unreadMessagesCount in [YDNetworkingManager sharedNetworkingManager].badgeNumbers) {
if (unreadMessagesCount.intValue > 0) {
NSInteger index = [[YDNetworkingManager sharedNetworkingManager].badgeNumbers indexOfObject:unreadMessagesCount];
self.segmentedHeader.selectedIndex = index;
[self loadTableViewAtIndex:index];
break;
}
}
}
}
The code of my private method is above. This controller has the property SegmentedHeader to switch between different kinds of messages.
This method is called After the Controller which used this method as addSubView;
viewDidLoad method is called when view of Controller is preparing to be addSubView into another view. And when view of Controller was added, viewWillAppear and viewDidAppear will be called. If view of Controller is removed from superview, viewWillDisAppear and viewDidDisAppear will be called.
And view of Controller is added again, viewDidLoad will NOT be called. Other methods will call the same as I've said.
It means when you initial a Controller, viewDidLoad was not called immediately. This method will be called later. It is lazy-loading
Hope it useful!
ViewController initialization is not creating view - viewcontroller's view is loaded lazily - when it's view is actually added to view hierarchy. So, -viewDidLoad method gets called when you access viewcontroller's .view property - thus, you may consider initializing your data structures in the init method (fetch data, allocate containers, setup default values, etc.) and later, when -viewDidLoad is called - apply you data to your views / UI.
I am designing a one-to-one chatting interface using table view. This table view is modified to show bubbles as some new message arrives. The new message arrives through a push-notification in this case. I call following code in my function which receives message through the push notification:
-(void)messageReceived: (NSString *)message{
_message=[message retain];
[tableView reloadData];
}
However, it seems this does not reload my table view.
If I place the call for reloadData in the viewDidAppear function, it reloads fine. It also reloads fine, if I place the reloadData call in a function whose return type is IBAction (ex: a function binding to button click)
What could be the reason for reloadData to not get triggered through custom declared functions ?
reloaddata method is called but the trick here that you didn't add the incoming message to the datasource that the tableview load from !
may be you have not Connect the Tableview with table view Delegates and Datasource
Objective-C
#interface YourClass : UIViewController <UITextFieldDelegate, UITextViewDelegate>
yourtableview.delegate = self;
yourtableview.dataSource = self;
[tableView reloadData];
Swift 3
class YourClass: UIViewController , UITableViewDelegate, UITableViewDataSource
yourtableview.delegate = self
yourtableview.dataSource = self
yourtableview.reloadData()
the other way is! for Swift and Objective-C both.
Right Click on the Table view and drag and drop the delegates.