How to perform s segue in this custom disclosure button? - ios

I am a new bee in IOS development, I found JPSThumbnailAnnotation in github, which really inspired me.
https://github.com/jpsim/JPSThumbnailAnnotation
My question is how to perform a segue in this custom disclosure button?
[self performSegueWithIdentifier:#"XXX" sender:self];
can not be preformed in - (void)didTapDisclosureButton
Thanks!

ok, since you have not specified what - (void)didTapDisclosureButton is, there you go.
On the page you linked to, I found this example
JPSThumbnail *thumbnail = [[JPSThumbnail alloc] init];
thumbnail.image = [UIImage imageNamed:#"empire.jpg"];
thumbnail.title = #"Empire State Building";
thumbnail.subtitle = #"NYC Landmark";
thumbnail.coordinate = CLLocationCoordinate2DMake(40.75f, -73.99f);
thumbnail.disclosureBlock = ^{ NSLog(#"selected Empire"); };
[mapView addAnnotation:[JPSThumbnailAnnotation annotationWithThumbnail:thumbnail]];
So why not:
thumbnail.disclosureBlock = ^{
[self performSegueWithIdentifier:#"XXX" sender:self];
};
As specified on that same page
You can also set a block to be run when the disclosure button is tapped.

Related

Correct way to handle application launching with quick actions

I am having a hard time figuring out how to get my quick actions working when I launch my app with a quick action.
My quick actions work, however, if the app was in the background and re-launched with the quick action.
When I try to launch the app straight from the quick action, the app opens as if it was launched by simply tapping the app icon (i.e. it does nothing).
Here is some code from my App Delegate.
In didFinishLaunchingWithOptions:
UIApplicationShortcutItem *shortcut = launchOptions[UIApplicationLaunchOptionsShortcutItemKey];
if(shortcut != nil){
performShortcutDelegate = NO;
[self performQuickAction: shortcut fromLaunch:YES];
}
The method called:
-(BOOL) performQuickAction: (UIApplicationShortcutItem *)shortcutItem fromLaunch:(BOOL)launchedFromInactive {
NSMutableArray *meetings = [self.fetchedResultController.fetchedObjects mutableCopy];
[meetings removeObjectAtIndex:0];
unsigned long count = meetings.count;
BOOL quickActionHandled = NO;
if(count > 0){
MainViewController *mainVC = (MainViewController *)self.window.rootViewController;
if(launchedFromInactive){
mainVC.shortcut = shortcutItem;
}
else{
UINavigationController *childNav;
MeetingViewController *meetingVC;
for(int i = 0; i < mainVC.childViewControllers.count; i++){
if([mainVC.childViewControllers[i] isKindOfClass: [UINavigationController class]]){
childNav = mainVC.childViewControllers[i];
meetingVC = childNav.childViewControllers[0];
break;
}
}
self.shortcutDelegate = meetingVC;
if ([shortcutItem.type isEqual: #"Meeting"]){
NSNumber *index = [shortcutItem.userInfo objectForKey:#"Index"];
[self.shortcutDelegate switchToCorrectPageWithIndex: index launchedFromInactive:NO];
quickActionHandled = YES;
}
}
}
The only action that needs to be performed is that my page view controller (which is embedded inside the meetingVC) should switch to a certain page with respect to the shortcut chosen.
Any ideas on what causes the shortcut to not do anything when using it to launch as opposed to re-opening the app from the background??
I came to realize I was trying to call my methods on a view controller that was not in memory yet. This was causing bizarre behavior in my app. I did have the correct approach to getting access to the view controller and then it dawned on me the possibility of trying to execute the code using GCD.
__block MeetingViewController *safeSelf = self;
contentVC = [self initializeContentViewController: self.didLaunchFromInactive withPageIndex: intIndex];
NSArray *viewControllers = #[contentVC];
dispatch_async(dispatch_get_main_queue(), ^{
[safeSelf.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
});
The above worked like magic, and the shortcuts are leading to the correct page. Using a similar approach to mine hopefully yields the desired results for anyone else who wanted to get their shortcuts working by launching the app.

UILabel is not showing in neither in simulator or iPad

So i am showing a model controller on top of a view controller. And i have texts in the model controller, but somehow the texts are not visible. I tried everything but somehow labels are not visible. But of you stay on the page for like 30 -40 sec the text shows up. Also this model controller is called from main view controller after a successful service(REST) call. If i call the model without making the service call then labels are visible in simulator/iPad both. But if i call it after service call inside success block then labels are not visible. I tried adding the text programmatically but still same issue. I tried debugging using Color blended layers, but the label is not at all visible in the view somehow. :(
[self.serviceManager getCustDetails:account successBlock:^(NSDictionary * successDict) {
[self hideLoadingAnimation];
NSDictionary *custData = [[successDict objectForKey:#"txnData"] objectForKey:#"custData"];
self.showCurrYear = [iraContribData objectForKey:#"showCurrYear"];
if ([self.showCurrYear isEqual: #"true"]){
[self performSegueWithIdentifier:#"CSegue" sender:self];
}
} failureBlock:^(NSDictionary * failureDict) {
[self hideLoadingAnimation];
NSLog(#"Failiure Dict %#",failureDict);
}];
And this prepareForSegue method, -
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"CSegue"]) {
CustViewController *cVC = segue.destinationViewController;
cVC.delegate = self;
[cVC setModalPresentationStyle:UIModalPresentationFormSheet];
cVC.preferredContentSize = CGSizeMake(800,750);
}
}
Below is my screen in storyboard, but in simulator the label is not visible, only continue and close button is visible.
Please help!, any suggestions are most welcome. Thanks!
It is possible that the delay is due to a user interface update not made on the main thread.
Try to make sure that your code is executed on the main thread using dispatch_async like this :
[self.serviceManager getCustDetails:account successBlock:^(NSDictionary * successDict) {
dispatch_async(dispatch_get_main_queue(), ^{
[self hideLoadingAnimation];
NSDictionary *custData = [[successDict objectForKey:#"txnData"] objectForKey:#"custData"];
self.showCurrYear = [iraContribData objectForKey:#"showCurrYear"];
if ([self.showCurrYear isEqualToString:#"true"]){
[self performSegueWithIdentifier:#"CSegue" sender:self];
}
});
} failureBlock:^(NSDictionary * failureDict) {
dispatch_async(dispatch_get_main_queue(), ^{
[self hideLoadingAnimation];
NSLog(#"Failiure Dict %#",failureDict);
});
}];

Weird delay in UIPopoverController dismissal

Maybe this is purely simulator related. I have not tried it on an actual device yet.
I'm on the latest greatest MacBook with a 1TB flash drive, and 95% free processor, and less than full memory consumption.
I have a UIPopoverController with 4 items in it, sized to those items.
There's nothing complicated or multi-threaded or long running in any way associated in the UIPopoverController in question.
I've set the appear and dismiss animation at 0, yet when I tap on an item in the list, there seems to be an random indeterminate delay between 0 and .4 seconds in the popover disappearing. Of course the 0 is expected, but the times when it's nearly a half second is very noticeably longer and disconcerting.
Any idea what may be causing that?
Code that shows the popover...
-(IBAction)theLandImpsButtonPressed:(UIButton *)sender
{
iRpNameValuePopover *thePopoverContent = [[iRpNameValuePopover alloc] init];
thePopoverContent.theTableValues = [self getLandImpsChoicesList];
impsLandPopover = [[UIPopoverController alloc] initWithContentViewController:thePopoverContent];
thePopoverContent.thePopoverController = impsLandPopover;
impsLandPopover.popoverContentSize = [iRpUIHelper sizeForPopoverThatHasTitle:NO andListContent:thePopoverContent.theTableValues];
impsLandPopover.delegate = self;
[impsLandPopover presentPopoverFromRect:self.theLandImpsButton.bounds inView:self.theLandImpsButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
Code that dismisses the popover...
BTW, there is no evaluation time incurred here [self userChoiceIsValid] because it simply returns YES right now.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_theChosenNameValueItem = [self.theTableValues objectAtIndex:indexPath.row];
[self acceptUserChoiceAndClose];
}
// This contentViewController is encapsulated INSIDE a UIPopoverViewController, and this class cannot itself
// close the popover which contains it, hence the need for the reference to the popover controller
// It is the popover's delegate... the one that created the popover, that is able to close it.
-(void)acceptUserChoiceAndClose
{
_theUserChoseAValue = NO; // Start by assuming they didn't chose a valid value.
if ([self userChoiceIsValid])
{
// Set variable that indicates the user chose a value which can be saved to core data, and/or presented on screen.
_theUserChoseAValue = YES;
// Close the popover.
[_thePopoverController dismissPopoverAnimated:NO];
// Notify the class that presented the popover that the popover has been dismissed.
// It will still be available to the dismissal method where code can retrieve the user's choice, and set the popover to nil.
if (_thePopoverController.delegate && [_thePopoverController.delegate respondsToSelector:#selector(popoverControllerDidDismissPopover:)])
{
[_thePopoverController.delegate popoverControllerDidDismissPopover:_thePopoverController];
}
}
else
{
[self showValidationFailureMessageToUser];
}
}
Dismissing the viewController in main thread will solve the issue.
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
});
I would check it out in the profiler and see what the time is being spent on.
There's a good tutorial here.
UIPopoverPresentationController *popOverView;
//////
Dismiss it...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[popOverView.presentedViewController dismissViewControllerAnimated:NO completion:nil];
popOverView = nil;
});
});

Passing an animating UIImageView between viewControllers in prepareForSegue:?

Based on some earlier questions (ex: ios5 how to pass prepareForSegue: a UIImageView) I was wondering how smooth this technique is, say when doing a modal segue with animated = NO.
I can toss together a test but I imagine someone has already scoped out the performance and practical pitfalls of using this as a transition technique. I would be interested in hearing any details - for example, does the UIImageView animation restart or does it continue apace?
If you pass an animating image view to another controller, it looks like the animation continues apace. I can't see any "hitch" in the animation. Going forward with a modal presentation worked fine. I couldn't go back to the first controller, by just dismissing, I had to add the image view back to the view to make that work, and even then it only worked if I turned off auto layout (otherwise I get an error saying it can't install a constraint). So, to go back and forth with modals (with no animation) I had this in the first controller:
- (void)viewDidLoad {
[super viewDidLoad];
self.isFirst = YES;
NSMutableArray *imgArray = [NSMutableArray array];
for (int i = 1; i< 41; i++) {
UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:#"New_PICT%04d.jpg",i]];
if (img) {
[imgArray addObject:img];
}
}
self.iv.animationImages = imgArray;
self.iv.animationDuration = 10;
[self.iv startAnimating];
}
-(void)viewDidAppear:(BOOL)animated {
if (self.isFirst) {
self.isFirst = NO;
}else{
self.iv.frame = CGRectMake(48, 48, 48, 48);
[self.view addSubview:self.iv];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *sec = segue.destinationViewController;
sec.iv = self.iv;
}

Allow User to Cancel MBProgressHUD when JSON call takes too long

I've read and read on SO about this, and I just can't seem to find anything that matches my situation.
I've got MBProgressHUD loading when the view appears, as my app immediately goes to grab some webservice data. My problem is the back button on my navigationcontroller is unresponsive while the HUD is displayed (and therefore while the app gets its data). I want the user to be able to tap to dismiss (or to be able to hit the back button in the worst case) to get the heck out, if it's an endless wait. Here's my code that runs as soon as the view appears:
#ifdef __BLOCKS__
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.labelText = #"Loading";
hud.dimBackground = NO;
hud.userInteractionEnabled = YES;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do a task in the background
NSString *strURL = #"http://WEBSERVICE_URL_HERE";
//All the usual stuff to get the data from the service in here
NSDictionary* responseDict = [json objectForKey:#"data"]; // Get the dictionary
NSArray* resultsArray = [responseDict objectForKey:#"key"];
// Hide the HUD in the main tread
dispatch_async(dispatch_get_main_queue(), ^{
for (NSDictionary* internalDict in resultsArray)
{
for (NSString *key in [internalDict allKeys])
{//Parse everything and display the results
}
}
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
});
});
#endif
Leaving out all the gibberish about parsing the JSON. This all works fine, and the HUD dismisses after the data shows up and gets displayed. How in the world can I enable a way to stop all this on a tap and get back to the (blank) interface? GestureRecognizer? Would I set that up in the MBProgressHUD class? So frustrated...
Kindest thanks for any help. My apologies for the long post. And for my ugly code...
No need to extend MBProgressHUD. Simply add an UITapGestureRecognizer to it.
ViewDidLoad
:
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:NO];
HUD.mode = MBProgressHUDModeAnnularDeterminate;
UITapGestureRecognizer *HUDSingleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(singleTap:)];
[HUD addGestureRecognizer:HUDSingleTap];
And then:
-(void)singleTap:(UITapGestureRecognizer*)sender
{
//do what you need.
}
The MBProgressHUD is just a view with a custom drawing to indicate the current progress, which means it is not responsible for any of your app's logic. If you have a long running operation which needs to be canceled at some point, you have to implement this yourself.
The most elegant solution is to extend the MBProgressHUD. You can either draw a custom area which plays the role of a button, add a button programmatically or just wait for a tap event on the whole view. Then you can call a delegate method whenever that button or the view is tapped.
It can look like this:
// MBProgressHUD.h
#protocol MBProgressHUDDelegate <NSObject>
- (void)hudViewWasTapped; // or any other name
#end
// MBProgressHUD.m
// Either this, or some selector you set up for a gesture recognizer
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self.delegate respondsToSelector:#selector(hudViewWasTapped)]) {
[self.delegate performSelector:#selector(hudViewWasTapped)];
}
}
you have to set your view controller as the delegate for theMBProgressHUD and act accordingly.
Let me know if you need more clarification on this :)
To have extra information:
You could create contentView in your view
And simply show the hud in your contentView (not in your self.view or self.navigationController.view)
in this way your navigationBar's view will not be responsible for your hudView. So, you can go back from your navigationController's view to previous page.

Resources