How to call PopOver Controller from UITableViewCell.accessoryView? - objective-c

First I would like to say that I'm really new to ipad/ipod/iphone development, and to objective-c too.
With that being said, I'm trying to develop a small application targeting the iPad, using Xcode and IB, basically, I have a table, for each UITableViewCell in the table, I added to the accessoryView a button that contains an image.
Here is the code:
UIImage *img = [UIImage imageNamed:#"myimage.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(0.0, 0.0, img.size.width, img.size.height);
button.frame = frame; // match the button's size with the image size
[button setBackgroundImage:img forState:UIControlStateNormal];
// set the button's target to this table view controller so we can interpret touch events and map that to a NSIndexSet
[button addTarget:self action:#selector(checkButtonTapped:event:) forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;
So far, so good, now the problem is that I want a PopOver control to appear when a user taps the button on the accessoryView of a cell.
I tried this on the "accessoryButtonTappedForRowWithIndexPath" of the tableView:
UITableViewCell *cell = [myTable cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)cell.accessoryView;
//customViewController is the controller of the view that I want to be displayed by the PopOver controller
customViewController = [[CustomViewController alloc]init];
popOverController = [[UIPopoverController alloc]
initWithContentViewController: customViewController];
popOverController.popoverContentSize = CGSizeMake(147, 122);
CGRect rect = button.frame;
[popOverController presentPopoverFromRect:rect inView:cell.accessoryView
permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
The problem with this code is that it shows the Popover at the top of the application View, while debugging I saw the values of "rect" and they are:
x = 267
y = 13
so I think it is pretty obvious why the PopOver is being displayed so up on the view, so my question is, how can I get the correct values for the PopOver to appear just below the button on the accessoryView of the cell?
Also, as you can see, I'm telling it to use the "cell.accessoryView" for the "inView:" attribute, is that okay?

Try using button.bounds instead of button.frame since the rect is relative to the inView.
Also note that if the cell is near the bottom of the screen, the popover may not appear at the right size because you are forcing the arrow in the Up direction. You should either handle this manually or just use Any.

[popOverController presentPopoverFromRect:rect inView:cell.accessoryView
permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
Changing inView:cell.accessoryView to inView:cell

here's for swift:
let cell = tableView.cellForRowAtIndexPath(indexPath)
let rect = tableView.convertRect(tableView.rectForRowAtIndexPath(indexPath), toView: tableView)
var popoverContent = self.storyboard?.instantiateViewControllerWithIdentifier("loadZone") as! UIViewController
var nav = UINavigationController(rootViewController: popoverContent)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nav.popoverPresentationController
popoverContent.preferredContentSize = CGSizeMake(100,200)
popover!.sourceView = tableView
popover!.sourceRect = rect
self.presentViewController(nav, animated: true, completion: nil)

Related

Remove subview when touched outside of subview

My situation is a little more complicated than the others listed.
I have a UITableView that takes up most of the screen.
Each row pops up a subview that contains more profile information. When the screen is clicked again this subview disappears. This works perfectly.
In the Navigation Bar I have a button that will display a small menu.
- (IBAction)menuButtonClicked:(UIBarButtonItem *)sender {
//If menuView exists and Menu button is clicked, remove it from view
if (self.menuView) {
self.tableView.userInteractionEnabled = true;
[self.menuView removeFromSuperview];
self.menuView = Nil;
}
//Menu View doesn't exist so create it
else {
// Create the Menu View and add it to the parent view
self.menuView = [[[NSBundle mainBundle] loadNibNamed:#"MenuView" owner:self
options:nil] objectAtIndex:0];
self.menuView.layer.cornerRadius = 20.0f;
self.menuView.layer.borderWidth = 3.0f;
self.menuView.layer.borderColor = [UIColor whiteColor].CGColor;
self.menuView.frame = CGRectMake(0, 64, self.menuView.frame.size.width,
self.menuView.frame.size.height);
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[self.menuView addGestureRecognizer:singleTap];
//Disable Selection of Profiles while Menu is showing
self.tableView.userInteractionEnabled = false;
//Add MenuView to View
[self.view addSubview: self.menuView];
}
}
//Removed Sub Views from View when tapped
-(void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture{
if(self.profileView){
[self.profileView removeFromSuperview];
self.profileView = Nil;
}
if(self.menuView) {
self.tableView.userInteractionEnabled = true;
[self.menuView removeFromSuperview];
self.menuView = Nil;
}
}
Now I want to dismiss this menus if the menu button is clicked again (working in above code) but also when the user touches out of the menu and on the tableView or navbar. If the menu is displayed, I don't want the tableView to display it's profile subview (working in above code) but just remove the menuView. I can't get the menuView to go away if I touch the tableView.
Can anyone point me in the right direction?
Make a new transparent overlay view sized to cover the entire screen. Add your menuView as a subview of the overlay, then add the overlay as a subview of your main window. Put a tap gesture recognizer on the overlay that will dismiss it when tapped.
You may need to set cancelsTouchesInView to NO on your gesture recognizer if buttons on your menu view are not working.
Roughly this (please excuse typos, I haven't compiled this):
- (void)showMenu
{
self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
overlay.backgroundColor = [UIColor clearColor];
self.menuView = /* code to load menuView */;
[overlay addSubview:self.menuView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(onSingleTap:)];
tap.cancelsTouchesInView = NO;
[overlay addGestureRecognizer:tap];
[self.tableView.window addSubview:overlay];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)sender
{
[self.overlay removeFromSuperview];
}
You might also want to add a swipe gesture recognizer to also dismiss the overlay, as someone may attempt to scroll the table expecting the menu to be dismissed.
While trying to make my own custom topdown-slide menu using my own custom NIB file, I found that this can be achieved by many techniques. I would like to suggest and share a different solution which is very similar but is created with a custom button on the background.
I've been looking around but could not find answers mentioning this.
This is very similar to the tap recogniser except for one thing - tap recogniser spreads all over the layout (including subviews), while using a layer of custom button allows you to interact with the top view and dismiss/ remove it from superview when clicking on lower layer (when lower layer is the background button). This is how I did it:
You create the layout
You add a UIButton with type UIButtonTypeCustom to the layout
You frame this layout over the view you wish to be responsive to that tap/click
You add your menu view on top of that layout and animate your menu to appear
- (void)showMenuViewWithBackgroundButtonOverlay
{
self.backgroundButton = ({
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = self.view.frame;
[button addTarget:self action:#selector(toggleAppMenu) forControlEvents:UIControlEventTouchUpInside];
button;
});
if (!self.menu) {
self.menu = [self createMenu]; // <-- get your own custom menu UIView
}
if (!self.overlay) {
self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.overlay.backgroundColor = [UIColor clearColor];
[self.overlay addSubview:self.backgroundButton];
[self.overlay addSubview:self.menu];
[self.view addSubview:self.overlay];
}
[self toggleAppMenu];
}
And the toggleAppMenu:
- (void)toggleAppMenu
{
CGRect nowFrame = [self.menu frame];
CGRect toBeFrame = nowFrame;
CGFloat navHeight = self.navigationController.navigationBar.frame.size.height;
CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
if (self.showingMenu) {
toBeFrame.origin.y = toBeFrame.origin.y-nowFrame.size.height-navHeight-statusBarHeight;
[UIView animateWithDuration:0.5 animations:^{
[self.menu setFrame: toBeFrame];
}completion:^(BOOL finished) {
self.showingMenu = !self.showingMenu;
[self.view endEditing:YES];
[self.overlay removeFromSuperview];
self.overlay = nil;
NSLog(#"menu is NOT showing");
}];
}
else{
toBeFrame.origin.y = navHeight+statusBarHeight;
[UIView animateWithDuration:0.5 animations:^{
[self.menu setFrame: toBeFrame];
}completion:^(BOOL finished) {
self.showingMenu = !self.showingMenu;
NSLog(#"menu is showing");
}];
}
}
I hope this will be helpful for someone.
Works on Swift 5
I create custom view and I want it hide by tap outside subview. Maybe it can help for someone or anybody can suggest a better way :)
// create tap for view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(animateOut))
self.addGestureRecognizer(tapGesture)
// create tap for subview
let tapGesture2 = UITapGestureRecognizer(target: self, action: nil)
container.addGestureRecognizer(tapGesture2)

Root view controller is built in code - Wont resize for iPad

Im trying to convert my iphone app to ipad, all good until my root view controller. Its been built in code and no matter what I do I cant resize it/centre it. I have no nib for my Home page. Autoresize masks doesn't do anything, adding an iPad nib does nothing despite best efforts but continue to present the iphone screen in top left, and using if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) loads a a blank black iPad screen. I really dont know what to to now. If I set my screen elements to initWithFrame:[[UIScreen mainScreen] bounds]]; the artwork is distorted as its overstretched.
// Create the main view
UIView *mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
mainView.backgroundColor = [UIColor blackColor];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIImageView *textureBg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 768.0f, 1024.0f)];
textureBg.image = [UIImage imageNamed:#"BgTexture"];
[textureBg addSubview:textureBg];
}
else {
// Add textured background
UIImageView *textureBg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320.0f, 1024.0f)];
textureBg.image = [UIImage imageNamed:#"BgTexture"];
[textureBg addSubview:textureBg];
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIImageView *clipBg = [[UIImageView alloc] initWithFrame:CGRectMake(7.0f, 6.0f, 605.0f, 865.0f )];
clipBg.image = [UIImage imageNamed:#"BgHomeNew2"];
clipBg.userInteractionEnabled = YES;
}
else {
// Add clipboard background
UIImageView *clipBg = [[UIImageView alloc] initWithFrame:CGRectMake(7.0f, 6.0f, 305.0f, 465.0f )];
clipBg.image = [UIImage imageNamed:#"BgHomeNew2"];
clipBg.userInteractionEnabled = YES;
//[clipBg setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleTopMargin )];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Add about button to clipbg
UIButton *aboutButton = [UIButton buttonWithType:UIButtonTypeCustom];
aboutButton.frame = CGRectMake(240.0f, 90.0f, 31.0f, 33.0f);
[aboutButton setImage:[UIImage imageNamed:#"LabelButton"] forState:UIControlStateNormal];
[aboutButton addTarget:self action:#selector(aboutButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[aboutButton addSubview:aboutButton];
}
else {
// Add about button to clipbg
UIButton *aboutButton = [UIButton buttonWithType:UIButtonTypeCustom];
aboutButton.frame = CGRectMake(240.0f, 90.0f, 31.0f, 33.0f);
[aboutButton setImage:[UIImage imageNamed:#"LabelButton"] forState:UIControlStateNormal];
[aboutButton addTarget:self action:#selector(aboutButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[aboutButton addSubview:aboutButton];
}
How do I make an ipad view, for a universal app, where the root view controller is built in code? Im even open to the idea of simply centering the iphone screen in the middle of the ipad screen but as autoresing wont work its just stuck in the top left. This would be 2 mins in IB!
To summarise why wont the following code move the image to the center of the iPad screen?
UIImageView *clipBg = [[UIImageView alloc] initWithFrame:CGRectMake(7.0f, 6.0f, 305.0f, 465.0f )];
clipBg.image = [UIImage imageNamed:#"BgHomeNew2"];
clipBg.userInteractionEnabled = YES;
[clipBg setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin)];
EDIT
Really dont know what im missing here, been on this a whole day now, Cant seem to add a xib retrospectively to an iphone screen built in code, so I will settle for centring objects in the middle of the screen when ipad is used rather than stuck in the top left all the time. That dosent work either.
Ok sorted it
Ive finally maged to add xibs now so I can adjust as necessary, I couldn't initialise the xibs before because I had missed this line of code in my rootview contoller
- (id)init
{
[[NSBundle mainBundle] loadNibNamed:#"ICHomeController" owner:self options:nil];

UINavigationBar - change UIBarButtonItem positions

I am using a UINavigationController and its bar within my app.
Now I want to change the leftBarButtonItem and rightBarButtonItem positions.
I want them to be on a different x and y position having a custom witdth and height.
But the problem is, that changing the frame does not change anything with the barButtonItem. I do not now why, but the frame always gets resetted to its original coordinates.
Here is my code which I call in viewWillAppear:
UINavigationItem *navItem = [[self.navigationBar items] lastObject];
UIView *rightview = [[UIView alloc] initWithFrame:CGRectMake(0,0,66,30)];
rightview.backgroundColor = [UIColor blueColor];
//add custom view
UIBarButtonItem *search = [[UIBarButtonItem alloc] initWithCustomView:rightview];
navItem.leftBarButtonItem = search;
The view is not starting at 0/0, the x position for example is at 5px insetead of 0.
Any suggestions on how to solve this issue?
This worked for me for the leftBarButtonItem:
If you want the UIBarButtonItem to show exactly at position (0, 0), you can create a UIButton, sets its frame to (-5, 0, width, height), assuming that the offet of a left barbutton item is (5, 0), and place that in a UIView that you set as leftBarButtonItem.
#import "UINavigationItem+CSAButtons.h"
#implementation UINavigationItem (CSAButtons)
- (void) makeLeftBarButtonWithTarget:(id)target action:(SEL)action {
UIImage * imageNormal = [UIImage imageNamed:#"normal.png"];
UIImage * imageHighlighted = [UIImage imageNamed:#"highlighted.png"];
UIImage * imageDisabled = [UIImage imageNamed:#"disabled.png"];
// create your button
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.exclusiveTouch = YES;
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundImage:imageNormal forState:UIControlStateNormal];
[button setBackgroundImage:imageHighlighted forState:UIControlStateHighlighted];
[button setBackgroundImage:imageDisabled forState:UIControlStateDisabled];
// set the frame of the button (better to grab actual offset of leftbarbuttonitem instead of magic numbers)
button.frame = CGRectMake(-5.0, 0.0, imageNormal.size.width, imageNormal.size.height);
UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, imageNormal.size.width, imageNormal.size.height)];
[view addSubview:button];
// set the barbuttonitem to be the view
UIBarButtonItem * barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
self.leftBarButtonItem = barButtonItem;
}
I dont think you have the ability to move a UIBarButtonItem but you might be able to achieve the same effect by adding that UIView element as a subview to the navigationBar (or navItem). You'll need to play with it a bit. It should allow for much more flexibility.
hope this helps.

UINavigationController rightbarbuttonitem not showing

after going through the questions related to UINavigationController item i came to post my exact problem ..
firstly i am not add a UINavigationController in MainWindow.xib i created and adding UINavigationController in AppDelegate file
after that in a UIViewController (not in rootViewController) class I have to add a rightbarbutton of Done type I am
adding it in a searchbarDelegate Method showing as bold:-
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
//This method is called again when the user clicks back from teh detail view.
//So the overlay is displayed on the results, which is something we do not want to happen.
if(exhibDataObj.searching)
return;
//Add the overlay view.
if(ovController == nil)
ovController = [[OverlayViewController alloc] initWithNibName:#"OverlayView" bundle:[NSBundle mainBundle]];
CGFloat yaxis = self.navigationController.navigationBar.frame.size.height;
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
//Parameters x = origion on x-axis, y = origon on y-axis.
CGRect frame = CGRectMake(0, yaxis, width, height);
ovController.view.frame = frame;
ovController.view.backgroundColor = [UIColor grayColor];
ovController.view.alpha = 0.5;
ovController.rvController = self;
[self.theTableView insertSubview:ovController.view aboveSubview:self.parentViewController.view];
exhibDataObj.searching = true;
letUserSelectRow = NO;
self.theTableView.scrollEnabled = NO;
//Add the done button.
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(doneSearching_Clicked:)];
}
but this never works .
Any help will be more appreciable.
I've tested your code real quick and it seems to be working fine for me, however, I've had the same problem once back then it turned out I was setting the rightBarButtonItem to nil in my viewDidLoad method after setting it to a button. you should make sure your not making the same mistake somehow.
However if that does not work for you you can always try setting a custom button to it:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 5, 30, 30)];
button.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"done.png"]];
[button addTarget:self action:#selector(doneButtonClicked) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationController.visibleViewController.navigationItem.rightBarButtonItem = customItem;

Button not clickable after adding as a subview to a UIView

I have created a class called UICustomButton, which is a subclass of UIView. I added a UIButton to UIView as a subview as shown in my code below:
-(id)initWithButtonType:(NSString *)type
{
self = [super init];
if (self)
{
self.customButton = [self setupButtonWithTitle:type andFrame:frame];
[self addSubview:self.customButton];
self.customButton addTarget:self action:#selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
The problem I am facing is that although the button appears, they are not clickable. Is there something wrong with the way I am adding the buttons to the UIView?
EDIT: To add on, I am using this custom button class instance to a cell:
UICustomButton *customButton = [[UICustomButton alloc]initWithFrame:someFrame];
[cell.contentView addSubView:customButton];
check the frame of your UICustomButton object, and if self.customButton is out of its superView.
Make sure that the frame of the subview is within the frame of its superview. I encountered the issue twice and both times the frame of the subview was incorrect.
You should check if all properties userInteractionEnabled are on:
Check that property for UITableView where your cells with this view are displayed
Check that property for UITableViewCell where you add this view as subview
Check that property for UICustomButton
Check that property for customButton in -(id)initWithButtonType:(NSString *)type
My button was not working because i had two views in each other. The first view, as you can see, has a width and heigth of 0 pixels. I've made this view the same size as view2 and then my button was clickable.
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(325, 346, 0, 0)];
view1.alpha = 0.90;
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 450, 150)];
[view2.layer setCornerRadius:10.0f];
view2.backgroundColor = [UIColor whiteColor];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self action:#selector(addClosedToCollection) forControlEvents:UIControlEventTouchUpInside];
[btn setFrame:CGRectMake(30, 30, 300, 32)];
[btn setTitle:#"i'm a button" forState:UIControlStateNormal];
[view2 addSubview:btn];
[view1 addSubview:view2];
i hope i've helped somebody out :)
Your outer view probably has a height of 0. Add constraints that makes it the same height as (or higher than) the subviews, or create it with a frame that is large enough.
try putting this after the if statement:
self.isTouchEnabled = YES;