I use MBProgressHUD when loading data and user can press the another tab button during the process. MBProgressHUD only disable the view contents.
I checked other posts but didn't see anything helps me to disable the tab button.
I tried to set tabbaritem.userInteractionEnabled to NO but I couldn't find a way to access that. I can do it in storyboard but can't switch it back to YES.
My question is; from my viewController is there any way to access tabbarcontroller.tabbaritem.userInteractionEnabled ?
I use category:
UIViewController+MBProgressHUD.h
#import <UIKit/UIKit.h>
#class MBProgressHUD;
#interface UIViewController (MBProgressHUD)
- (MBProgressHUD *)showHUD;
- (MBProgressHUD *)showHUDFromTitle:(NSString *)title;
- (MBProgressHUD *)showHUDFromTitle:(NSString *)title completedImage:(BOOL)completedImage;
- (void)hideHUD;
#end
And UIViewController+MBProgressHUD.m
#import "UIViewController+MBProgressHUD.h"
#import <MBProgressHUD/MBProgressHUD.h>
#implementation UIViewController (MBProgressHUD)
- (MBProgressHUD *)showHUDFromTitle:(NSString *)title {
UIView *view;
if (self.tabBarController.view != nil) {
view = self.tabBarController.view;
} else if (self.navigationController.view != nil) {
view = self.navigationController.view;
} else {
view = self.view;
}
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:NO];
hud.labelText = title;
return hud;
}
- (MBProgressHUD *)showHUD {
return [self showHUDFromTitle:NSLocalizedString(#"Loading", #"Loading")];
}
- (MBProgressHUD *)showHUDFromTitle:(NSString *)title completedImage:(BOOL)completedImage {
MBProgressHUD *hud = [self showHUDFromTitle:title];
if (completedImage) {
UIImage *checkmarkImage = [UIImage imageNamed:#"37x-Checkmark"];
UIImageView *checkmarkImageView = [[UIImageView alloc] initWithImage:checkmarkImage];
hud.customView = checkmarkImageView;
hud.mode = MBProgressHUDModeCustomView;
} else {
hud.mode = MBProgressHUDModeText;
}
return hud;
}
- (void)hideHUD {
[MBProgressHUD hideAllHUDsForView:self.tabBarController.view animated:NO];
[MBProgressHUD hideAllHUDsForView:self.navigationController.view animated:NO];
[MBProgressHUD hideAllHUDsForView:self.view animated:NO];
}
Example:
[self showHUD];
[self hideHUD];
This is an easy way
[[[self tabBarController] tabBar] setUserInteractionEnabled:NO];
As stated in this link: How can I make the tabbar action hidden when the view is loading?
Works great with MBProgressHUD
Even more elegant to show the HUD from UITabBarController, since user interaction will be entirely controlled by MBProgressHUD until hideAnimated: is called:
// Show HUD from Tab Bar, to disable the tab bar items:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.tabBarController.view
animated:YES];
// Do some time-consuming stuff:
...
// Hide HUD and enable tab bar items:
[hud hideAnimated:YES];
Related
I have a tableview for showing a list of devices in my application. When viewWillAppear is called, I add the self.searchDisplayController.searchBar as a subview to a headerView. I then assign self.tableView.tableHeaderView = headerView. It looks like this:
I scroll the tableview down so that headerview goes out of view and then go to some other view controller by tapping on a cell. When I come back to this tableView, scroll up to the headerView, the searchBar becomes invisible, however on tapping the invisible area the searchDisplayController gets activated and the cancel button doesn't work. This happens for iOS 7 only. Why is this happening?
Note: It happens only if the headerView is out of the view when I come back to the tableViewController.
I've just had the same issue. When I go to debug into the delegate method of UISearchDisplayController at the end search state, the searchBar becomes a subview of an UIView, not the UITableView. Please see below code:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
//My Solution: remove the searchBar away from current super view,
//then add it as subview again to the tableView
UISearchBar *searchBar = controller.searchBar;
UIView *superView = searchBar.superview;
if (![superView isKindOfClass:[UITableView class]]) {
NSLog(#"Error here");
[searchBar removeFromSuperview];
[self.tableView addSubview:searchBar];
}
NSLog(#"%#", NSStringFromClass([superView class]));
}
My solution is remove the searchBar away from current super view, then add it as subview again to the tableView. I've already tested successfully.
Hope that help!
Regards
I have the exact same problem. the search bar is still there and can receive touch events. it is however not rendered. I believe the problem is in UISearchDisplaycontroller because it renders fine if I don't use UISearchDisplayController. I ended up writing a custom SearchDisplaycontroller to replace it. it is very basic and only does what I need.
use it is the same way as you would the normal UISearchDisplayController but self.searchDisplayController will not return anything. you will have to use another pointer to refer to the custom search display controller.
looks like a big ugly work around, but the only one that worked for me. keen to hear of alternatives.
#protocol SearchDisplayDelegate;
#interface SearchDisplayController : NSObject<UISearchBarDelegate>
- (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController;
#property(nonatomic,assign) id<SearchDisplayDelegate> delegate;
#property(nonatomic,getter=isActive) BOOL active; // configure the view controller for searching. default is NO. animated is NO
- (void)setActive:(BOOL)visible animated:(BOOL)animated; // animate the view controller for searching
#property(nonatomic,readonly) UISearchBar *searchBar;
#property(nonatomic,readonly) UIViewController *searchContentsController; // the view we are searching (often a UITableViewController)
#property(nonatomic,readonly) UITableView *searchResultsTableView; // will return non-nil. create if requested
#property(nonatomic,assign) id<UITableViewDataSource> searchResultsDataSource; // default is nil. delegate can provide
#property(nonatomic,assign) id<UITableViewDelegate> searchResultsDelegate;
#end
#protocol SearchDisplayDelegate <NSObject>
// implement the protocols you need
#optional
#end
the implementation
#implementation SearchDisplayController {
UISearchBar *_searchBar;
UIViewController *_viewController;
UITableView *_searchResultsTableView;
UIView *_overLay;
}
- (void)dealloc {
[_searchBar release];
[_searchResultsTableView release];
[_overLay release];
[super dealloc];
}
- (UIViewController *)searchContentsController {
return _viewController;
}
- (UITableView *)searchResultsTableView {
return _searchResultsTableView;
}
- (id)initWithSearchBar:(UISearchBar *)searchBar contentsController:(UIViewController *)viewController {
self = [super init];
if (self) {
_searchBar = [searchBar retain];
_searchBar.delegate = self;
_viewController = viewController;
_searchResultsTableView = [[UITableView alloc]initWithFrame:CGRectMake(0, CGRectGetMaxY(_searchBar.frame), _viewController.view.frame.size.width, _viewController.view.frame.size.height - CGRectGetMaxY(_searchBar.frame))];
_overLay = [[UIView alloc]initWithFrame:_searchResultsTableView.frame];
_overLay.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(overLayTapped)];
[_overLay addGestureRecognizer:tap];
[tap release];
}
return self;
}
- (void)setSearchResultsDataSource:(id<UITableViewDataSource>)searchResultsDataSource {
_searchResultsTableView.dataSource = searchResultsDataSource;
}
- (void)setSearchResultsDelegate:(id<UITableViewDelegate>)searchResultsDelegate {
_searchResultsTableView.delegate = searchResultsDelegate;
}
- (void)overLayTapped {
[self setActive:NO animated:YES];
[_searchBar resignFirstResponder];
_searchBar.text = nil;
_searchBar.showsCancelButton = NO;
}
- (void)setActive:(BOOL)visible animated:(BOOL)animated {
UIView *viewToAdd = nil;
if (!_searchBar.text.length) {
viewToAdd = _overLay;
} else {
viewToAdd = _searchResultsTableView;
}
float a = 0;
if (visible) {
[_viewController.view addSubview:viewToAdd];
a = 1.0;
}
if ([_viewController.view respondsToSelector:#selectore(scrollEnabled)]) {
((UIScrollView *)_viewController.view).scrollEnabled = !visible;
}
if (animated) {
[UIView animateWithDuration:0.2 animations:^{
_overLay.alpha = a;
_searchResultsTableView.alpha = a;
}];
} else {
_overLay.alpha = a;
_searchResultsTableView.alpha = a;
}
}
- (void)setActive:(BOOL)active {
[self setActive:active animated:YES];
}
#pragma mark - UISearchBar delegate protocols
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[self setActive:YES animated:YES];
searchBar.showsCancelButton = YES;
[_searchResultsTableView reloadData];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[_searchResultsTableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[self overLayTapped];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length) {
[_overLay removeFromSuperview];
[_viewController.view addSubview:_searchResultsTableView];
} else {
[_searchResultsTableView removeFromSuperview];
[_viewController.view addSubview:_overLay];
}
[_searchResultsTableView reloadData];
}
#end
Update: on how to use this progammatically
declare an ivar
SearchDisplayController *mySearchDisplayController;
initialize it programmatically
mySearchDisplayController = [[SearchDisplayController alloc]initWithSearchBar:mySearchBar contentsController:self];
adding the searchbar to your tableview
self.tableView.headerView = mySearchBar;
use mySearchDisplayController as reference to the custon class instead on self.searchDisplayController.
In my case, the table view that held the search display controller's search bar in its header view was being reloaded almost as soon as the view appeared. It was at this point that the search bar would cease to render. When I scrolled the table, it would reappear. It's also worth mentioning that my table contained a UIRefreshControl and was not a UITableViewController subclass.
My fix involved setting the search display controller active and then inactive very quickly just before after loading the table (and ending the refresh control refreshing):
[self.tableView reloadData];
[self.refreshControl endRefreshing];
[self.searchDisplayController setActive:YES animated:NO];
[self.searchDisplayController setActive:NO];
A bit of a hack but it works for me.
Using the debugger, I've found that the UISearchBar is initially a child view of the tableHeaderView - but when it disappears, it has become a child of the tableView itself. This has probably been done by UISearchDisplayController somehow... So I did the following hack to simply return the UISearchBar to the header view:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(!self.searchDisplayController.isActive && self.searchBar.superview != self.tableView.tableHeaderView) {
[self.tableView.tableHeaderView addSubview:self.searchBar];
}
}
Seems to work fine on iOS 7 as well as 6 :)
(checking that the searchDisplayController isn't active is necessary, otherwise the sarch bar disappears during search)
I endorse Phien Tram's answer. Please upvote it. I don't have enough points myself.
I had a similar problem where a search bar loaded from storyboard would disappear when I repeatedly tapped it, invoking and dismissing search. His solution repairs the problem.
There seems to be a bug where repeated invocation and dismissal of the search display controller doesn't always give the search bar back to the table view.
I will say I'm uncomfortable with the solution's dependence on the existing view hierarchy. Apple seems to reshuffle it with every major release. This code may break with iOS 8.
I think a permanent solution will require a fix by Apple.
I had the same issue and I could fix it calling next line after creating the UISearchDisplayController
[self performSelector:#selector(setSearchDisplayController:) withObject:displayController];
My viewDidLoad function look like this:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 44)];
searchBar.placeholder = NSLocalizedString(#"Search", #"Search");
UISearchDisplayController *displayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
displayController.delegate = self;
displayController.searchResultsDataSource = self;
displayController.searchResultsDelegate = self;
[self performSelector:#selector(setSearchDisplayController:) withObject:displayController];
self.tableView.contentOffset = CGPointMake(0, 44);
self.tableView.tableHeaderView = searchBar;
Thanks to this answer: https://stackoverflow.com/a/17324921/1070393
Use UISearchBar above UITableView,Then make IBOutlet for and connect them with file's owner to UISearchbar
Example- .h file
#import <UIKit/UIKit.h>
#interface LocationViewController : UIViewController<UISearchBarDelegate>
{
BOOL IsSearchOn;
}
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
#property (strong, nonatomic) IBOutlet UITableView *TBVLocation;
.m file
#pragma mark -
#pragma mark UISearchBar Delegate Methods
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[self.searchResult removeAllObjects];
if(searchText.length == 0)
{
IsSearchOn=NO;
// [filteredTableData removeAllObjects];
[self.searchBar resignFirstResponder];
// [self .tblView reloadData];
}
else
{
IsSearchOn=YES;
if(searchText != nil && ![searchText isEqualToString:#""])
{
/* NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#", searchText];
self.searchResult = [NSMutableArray arrayWithArray: [searchArray filteredArrayUsingPredicate:resultPredicate]];*/
for(int i=0;i<[[arrCountryList valueForKey:#"country_name"] count];i++)
{
NSRange titleRange = [[[[arrCountryList valueForKey:#"country_name"] objectAtIndex:i] lowercaseString] rangeOfString:[searchText lowercaseString]];
if(titleRange.location != NSNotFound)
[self.searchResult addObject:[arrCountryList objectAtIndex:i]];
}
[TBVLocation reloadData];
}
}
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
-(void)searchBarCancelButtonClicked:(UISearchBar *) searchBar
{
[searchBar resignFirstResponder];
IsSearchOn=NO;
searchBar.text = nil;
[TBVLocation reloadData];
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
return YES;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsCancelButton = YES;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
// IsSearchOn=YES;
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
IsSearchOn=NO;
searchBar.showsCancelButton = NO;
[TBVLocation reloadData];
[searchBar resignFirstResponder];
}
It will work like charm.
I've faced similar problem and after some digging, I've found that this is a bug in UISearchBar hierarchy. This hacky solution worked for me in iOS 7, but be aware that this may break in future iOS versions:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *buggyView = [self.searchDisplayController.searchBar.subviews firstObject];
// buggyView bounds and center are incorrect after returning from controller, so adjust them.
buggyView.bounds = self.searchDisplayController.searchBar.bounds;
buggyView.center = CGPointMake(CGRectGetWidth(buggyView.bounds)/2, CGRectGetHeight(buggyView.bounds)/2);
}
I had the same problem and tested some of the solutions proposed here in this thread, but they didn't solve the problem for me.
Previously, I added and configured the UISearchBar in the
- (void)viewDidLoad
method of my ViewController in code.
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:searchBarView.frame];
searchBar.delegate = self;
searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
ect...
What solved this issue for me was that I added a UISearchbar in the InterfaceBuilder, created an outlet in my ViewController and added this UISearchBar to my UISearchDisplayController.
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar(<--outlet) contentsController:self];
hope this might also help some people
I have a view in which I want to show loading animation. I have seen some application they are showing circular image to show loading, and the action will happen on background, Same thing I want to achieve here, Any inbuilt animation is available in IOS?
TIA
You can use the built in activity indicator.
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.center = CGPointMake(alert.bounds.size.width / 2 , (alert.bounds.size.height) /2);
[indicator startAnimating];
simply add it as a subview in to your view.
You may use the UIActivityIndicator if you want to keep things simple. Or there are plenty of open source activity indicators that do a lot of fancy stuff in addition to just showing a spinning wheel. MBProgressHUD and SVProgressHUD are two neat implementations.
Create YourViewController, and then add the the MBProgressHUB library to your project (you can get the library from here); download the project and move the library to your project.
Then you can use the following code to achieve your task:
YourViewController.h
#import <UIKit/UIKit.h>
#import "MBProgressHUD.h"
#interface YourViewController : UITableViewController <MBProgressHUDDelegate>
{
MBProgressHUD *hud;
}
YourViewController.m
#import "YourViewController.h"
#interface YourViewController ()
#end
#implementation YourViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self initializeProgressLoading];
[self getObjects];
[hud hide:YES afterDelay:1];
}
-(void) initializeProgressLoading {
hud = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:hud];
hud.delegate = self;
hud.labelText = #"Loading";
[hud showWhileExecuting:#selector(sleep) onTarget:self withObject:nil animated:YES];
}
- (void)sleep {
sleep(50000);
}
- (void) getObjects {
// connect to db and get all objects
//you can write any thing here
}
- (void)hudWasHidden:(MBProgressHUD *)hud1 {
// Remove HUD from screen when the HUD was hidded
[hud removeFromSuperview];
hud = nil;
}
I have a MBProgressHUD that I want to display when switching tabs. This is in my app delegate. I have this code here to display the HUD on only the first three tabs
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
if ([viewController isKindOfClass:[UINavigationController class]]) {
if (tabBarController.selectedIndex >= 3) {
UINavigationController *nav = (UINavigationController *) viewController;
[nav popToRootViewControllerAnimated:NO];
}
else {
UINavigationController *nav = (UINavigationController *) viewController;
HUD = [[MBProgressHUD alloc] initWithView:nav.view];
[nav.view addSubview:HUD];
HUD.labelText = #"Loading";
[HUD show:YES];
[nav popToRootViewControllerAnimated:NO];
}
}
}
The first time I view my page it works but going back to it a second time it doesn't hide. I have my [appDel.HUD hide:YES afterDelay:1.0]; in my viewDidAppear.
How can I get the HUD to hide every time I visit the page?
I think you may be adding the MBProgressHUD to a different view within the UINavigationController. Then you're popping to the root, and it's trying to remove an MBProgressHUD that doesn't exist. Try this (untested code)...
UINavigationController *nav = (UINavigationController *) viewController;
[nav popToRootViewControllerAnimated:NO];
if(![nav.visibleViewController.view.subViews containsObject:MBProgressHUD])
{
[MBProgressHUD showHUDAddedTo:nav.visibleViewController.view animated:YES];
MBProgressHUD.labelText = #"Loading";
}
else
{
[MBProgressHUD setHidden:NO];
}
you have to check if the MBProgressHUD view was already activated if so hid it, or simply do so:
if (!HUD) {
[self showLoadingHUD];
}
I have a custom UIButton class in my IPhone navigation application. When the user clicks the button I present a modal view which displays a tableview list of records that the user can select from.
On click of the button I create my viewController and show it using the following code:
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
dropDownPickerViewController = [[DropDownListingViewController alloc] initWithNibName:#"DropDownListingView" bundle:[NSBundle mainBundle]];
.....
.....
[[(AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate] navigationController] presentModalViewController:dropDownPickerViewController animated:NO];
[dropDownPickerViewController release];
[super touchesEnded:touches withEvent:event];
}
As you can see the button could be on any viewController so I grab the navigationController from the app delegate. Than in my DropDownPickerViewController code i have the following when a user selects a record:
[[(AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate] navigationController] dismissModalViewControllerAnimated:NO];
But nothing occurs. It seems to freeze and hang and not allow any interaction with the form. Any ideas on why my ModalViewController wouldnt be dismissing? Im thinking it is the way I use the app delegate to present the viewController. Is there a way to go self.ParentViewController in a UIButton class so I can get the ViewController of the view that the button is in (if this is the problem)?
Any ideas would be greatly appreciated.
Thanks
I did recall that I did have to do something similar once, and by looking at this post:
Get to UIViewController from UIView?
I was able to create some categories:
//
// UIKitCategories.h
//
#import <Foundation/Foundation.h>
#interface UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController;
- (id) traverseResponderChainForUIViewController;
#end
#interface UIView (FindUINavigationController)
- (UINavigationController *) firstAvailableUINavigationController;
- (id) traverseResponderChainForUINavigationController;
#end
//
// UIKitCategories.m
//
#import "UIKitCategories.h"
#implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
// convenience function for casting and to "mask" the recursive function
return (UIViewController *)[self traverseResponderChainForUIViewController];
}
- (id) traverseResponderChainForUIViewController {
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else if ([nextResponder isKindOfClass:[UIView class]]) {
return [nextResponder traverseResponderChainForUIViewController];
} else {
return nil;
}
}
#end
#implementation UIView (FindUINavigationController)
- (UINavigationController *) firstAvailableUINavigationController {
// convenience function for casting and to "mask" the recursive function
return (UINavigationController *)[self traverseResponderChainForUINavigationController];
}
- (id) traverseResponderChainForUINavigationController {
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UINavigationController class]]) {
return nextResponder;
} else {
if ([nextResponder isKindOfClass:[UIViewController class]]) {
//NSLog(#" this is a UIViewController ");
return [nextResponder traverseResponderChainForUINavigationController];
} else {
if ([nextResponder isKindOfClass:[UIView class]]) {
return [nextResponder traverseResponderChainForUINavigationController];
} else {
return nil;
}
}
}
}
#end
you can then call them like so:
UINavigationController *myNavController = [self firstAvailableUINavigationController];
UIViewController *myViewController = [self firstAvailableUIViewController];
maybe they will help you, I hope so.
In your DropDownPickerViewController, Instead of this:
[[(AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate] navigationController] dismissModalViewControllerAnimated:NO];
Try
[self dismissModalViewControllerAnimated:NO];
I would call a method on the button's "parent" viewController and do it from there.
I have a MKMapView (also a UIPopoverControllerDelegate) with Annotations. This MapView has, in the MKTestMapView.h file, a UIPopoverController* popoverController defined in the #interface and a #property (nonatomic, retain) UIPopoverController* popoverController; defined outside of the #interface section. This controller is #synthesized in the MKTestMapView.m file and it is released in the - (void)dealloc section. The Annotations in this MapView have rightCalloutAccessoryViews defined to the following:
- (void)mapView:(MKMapView *)mapView2 annotationView:(MKAnnotationView *)aview calloutAccessoryControlTapped:(UIControl *)control{
...
CGPoint leftTopPoint = [mapView2 convertCoordinate:aview.annotation.coordinate toPointToView:mapView2];
int boxDY=leftTopPoint.y;
int boxDX=leftTopPoint.x;
NSLog(#"\nDX:%d,DY:%d\n",boxDX,boxDY);
popoverController = [[UIPopoverController alloc] initWithContentViewController:controller];
popoverController.delegate = self;
CGSize maximumLabelSize = CGSizeMake(320.0f,600.0f);
popoverController.popoverContentSize = maximumLabelSize;
CGRect rect = CGRectMake(boxDX, boxDY, 320.0f, 600.0f);
[popoverController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
...
}
Now here comes the fun part. First of all, I am not sure if I need maximumLabelSize and the rect to be the same size. I am new to the popovercontroller so I am playing this by ear..
Okay, the popover shows. Now to dismissing it. I can click anywhere on mapView2 and the popover goes away...but I need the user to click a button in the view if they change anything. URGH!
The docs show:
To dismiss a popover programmatically,
call the dismissPopoverAnimated:
method of the popover controller.
Well, here is the problem: By definition of how the popoverController works, you are clicking inside the view of the displayed popover (to click the button) but have to trigger the dismissPopoverAnimated: method of the controller that launched this popover view, in my case, the popoverController inside the MKTestMapView.m file.
Now, having said all that, remember, [popoverController release] doesn't happen until:
- (void)dealloc {
[popoverController release];
[mapView release];
[super dealloc];
}
So, do i just do the following inside the button (messy but may work):
(Assuming my popover view is a TableView) In the:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
MKTestMapView * mKTestMapView = [[MKTestMapView alloc] init];
[[mKTestMapView popoverController].dismissPopoverAnimated:YES];
}
Here is my issue: I cannot figure out whether doing the above gives me a reference (if there is such a thing) to the existing view that is on the screen -- and therefore the view that is the owner of that popoverController. If it is as simple as
[[[self parentView] popoverController].dismissPopoverAnimated:YES];
I will shoot myself cos I don't think that is the correct syntax either!
This should be easy...yet I am lost. (probably just frustrated with so many iPad differences that I am learning).
Can anyone explain more?
I had the same problem... I had a neat "close" button (X) in the top of my view loaded by the popover, but it didn't work. In my universal app it will be presented as a new view, so that code should stay.
What I did now was that I added the following to my detailedPinView (the view the popover loads):
in the detailedPinView.h file:
#interface detailedPinView : UIViewController {
[...]
UIPopoverController *popover;
[...]
}
-(void)setPopover:(UIPopoverController*)aPopover;
In the detailedPinView.m file:
- (void)setPopover:(UIPopoverController*)aPopover
{
popover = aPopover;
}
The X button closes the view using an IBAction, this is what I did there:
In the detailedPinView.m file:
-(IBAction)releaseDetailedView:(UIButton *)sender
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
if (popover != nil)
{
[popover dismissPopoverAnimated:YES];
}
else {
NSLog(#"Nothing to dismiss");
}
}
else{
[self.parentViewController dismissModalViewControllerAnimated: YES];
}
}
In the class loading the my map and the popover view I added the following code:
[...]
-(void)mapView:(MKMapView *)theMapView annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control
{
UIViewController *detailController = [[detailedPinView alloc] initWithNibName:#"detailedPinView"
bundle:nil
annotationView:pin];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UIPopoverController* aPopover = [[UIPopoverController alloc] initWithContentViewController:detailController];
[aPopover setDelegate:self];
[aPopover setPopoverContentSize:CGSizeMake(320, 320) animated:YES];
[detailController setPopover:aPopover];
[detailController release];
[mapView deselectAnnotation:pin.annotation animated:YES];
self.popoverController = aPopover;
[mapView setCenterCoordinate:pin.annotation.coordinate animated:YES];
[self.popoverController presentPopoverFromRect:CGRectMake(382,498,0,0) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
else
{
[self presentModalViewController: detailController animated:YES];
}
[detailController release];
}
[...]
I don't know if the was the answer you were hoping for, I think it might be a bit of a messy way to do it... but giving the time schedule this worked like a charm :)
Here is another simple solution.
I found that we should follow the following steps for clearly dismissing popovers.
dismiss a popover.
release a view loaded by the popover.
Before iOS8, almost all of us may release a view loaded by a popover first, and then we programmatically dismiss the popover.
However, in iOS8, we do the steps in revers.
Before iOS8, my code of dismissing a popover
// creating a popover loading an image picker
picker = [[UIImagePickerController alloc] init];
...
pickerPopover = [[UIPopoverController alloc] initWithContentViewController:picker];
[pickerPopover presentPopoverFromRect:aFrame inView:aView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// dismissing the popover
[picker.view removeFromSuperview]; // (1) release a view loaded by a popover
[picker release], picker = nil;
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
[pickerPopover dismissPopoverAnimated:YES]; // (2) dismiss the popover
}
In iOS8, the dismissing code part should be changed as below,
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
[pickerPopover dismissPopoverAnimated:YES]; // (2) dismiss the popover first
}
[picker.view removeFromSuperview]; // (1) and then release the view loaded by the popover
[picker release], picker = nil;