iOS close popovercontroller from view controller - ios

I was digging around on SO and thought I had figured out how to do this but my test failed. I'm trying to close a popovercontroller from the viewcontroller that is launched/contained (I'm still a bit fuzzy on the poc and vc relationship) by it.
In my viewcontroller .h I have this:
#interface OAI_vcOperatingRooms : UIViewController {
OAI_ColorManager* colorManager;
OAI_FileManager* fileManager;
UIPopoverController* myPopOverController;
}
#property (nonatomic, weak) UIPopoverController* myPopOverController;
- (void) closeVC : (id) sender;
and in the .m file
UIButton* btnClose = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnClose addTarget:self
action:#selector(closeVC:)
forControlEvents:UIControlEventTouchDown];
[btnClose setTitle:#"Close" forState:UIControlStateNormal];
btnClose.frame = CGRectMake(10.0, 210.0, 160.0, 40.0);
[self.view addSubview:btnClose];
- (void) closeVC : (id) sender {
[myPopOverController dismissPopoverAnimated:YES];
}
in the uiview that calls the popovercontroller, I've referenced the viewcontroller and added this:
//operating rooms
controller2 = [[OAI_vcOperatingRooms alloc] initWithNibName:#"OAI_vcOperatingRooms" bundle:nil];
popoverController2 = [[UIPopoverController alloc] initWithContentViewController:controller2];
controller2.myPopOverController = popoverController2;
No errors but nothing happens when I hit the close button. Is it possible to reference a POC from within the VC?
Thanks

I believe ott’s comment is on to something - try using a strong property instead of a weak one. A weak property is likely to get set to nil by ARC immediately after its last use. A strong property will stick around for the lifetime of its parent object (in this case, your UIViewController subclass), unless you set it to nil early.
Note: you probably don’t need UIPopoverController *myPopOverController; in your interface, since Xcode will automatically generate _myPopOverController as a backing variable.

Related

what the difference between following situation in iOS?

i think there are two ways to add a UIControl in my view
status 1:
#property (nonatomic,weak) UIButton *button;
- (void)viewDidload
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button........ (set frame,color,text........)
[self.view addSubView:button];
_button = button;
}
status 2.
#property (nonatomic,strong) UIButton *button;
- (void)viewDidload
{
_button = [UIButton buttonWithType:UIButtonTypeCustom];
_button.......(set frame,color,text......)
[self.view addSubView:_button];
}
i want to know the difference between them and in different situation what should i chose ?
in version 1 (nonatomic, strong) the viewcontroller keeps a strong reference to the button. that means if you for example remove it from the superview somewhere in your code ([self.button removeFromSuperview];) it is still in memory and could be readded at a later point in time ([self.view addSubview:self.button];).
in version 2 (nonatomic, weak) the viewcontroller keeps a weak reference to the button. that means if you for example remove it from the superview somewhere in your code (and no other part of your app keeps a strong reference to it) it gets deallocated.
there is no real difference between creating a local variable UIButton *button = ... and assigning it to the instance variable afterwards _button = button; or working with the instance variable directly _button = [UIButton buttonWithType:....
in status 1:
you have marked you UIButton as weak Property
and A weak reference means the pointer has no owner, therefore it will be deallocated as soon as it is no longer needed (that is, nothing else is pointing to it).
but in status 2 :
you have marked you UIButton as Strong Property and when your Button hasn't mark as IBOutlet , you should use strong

Button is disabled when adding background

I am using Parse anypic tutorial and I want to create a somehow different UI. But I have some troubles.
So, my ViewController is this :
#interface PAPHomeViewController ()
#property (nonatomic, strong) PAPSettingsActionSheetDelegate *settingsActionSheetDelegate;
#property (nonatomic, strong) UIView *blankTimelineView;
#end
#implementation PAPHomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"LoadView is called");
// Present Anypic UI
[self presentUI];
}
-(void) presentUI {
/*UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
[backgroundImageView setImage:[UIImage imageNamed:#"DefaultAnypic.png"]];
self.view = backgroundImageView;*/
// Settings button
self.settingsButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.settingsButton addTarget:self
action:#selector(settingsButtonAction:)
forControlEvents:UIControlEventTouchUpInside];
[self.settingsButton setTitle:#"Settings" forState:UIControlStateNormal];
self.settingsButton.frame = CGRectMake(200.0, 200.0, 100.0, 100.0);
[self.view addSubview:self.settingsButton];
}
and its .h file is this :
#interface PAPHomeViewController : PAPPhotoTimelineViewController
#end
The PAPPhotoTimelineViewController is also a separateViewController, which the Home extends from and it is a tableViewController, which also calls ViewDidLoad.
The problem :
With the above Code, I see my button and I can click on my button.
But, if I uncomment stuff for the background, I do see the background, I do see the button, but it cannot be clicked - it is like no touch on the button is identified.
I am also confused, now that I am extending another ViewController which also implements viewDidLoad, why they are both called, in which order etc.
You shouldn't assign a new UIView to your self.view.
Instead of self.view = backgroundImageView;, just add it like a random view.
[self.view addSubview:backgroundImageView];
Doing that, you will follow the right way to add subview: your backgroundImageView will be displayed in your view, and your button will be add above it.

Trigger a method in UIViewController from its View

I have a UIViewController with its UIView which contains a UIButton. I want to trigger a method in UIViewController on button click event.
Keeping reference of UIViewController doesn't seem to be a good idea like the following link says:
Get to UIViewController from UIView?
So I want to achive this using a delegate. Any hint on how to achieve this?
You can do something like this
CustomView.h
#import <UIKit/UIKit.h>
#protocol CustomViewDelegate <NSObject>
-(void)didButtonPressed;
#end
#interface CustomView : UIView
#property (assign) id<CustomViewDelegate> delegate;
#end
CustomView.m
#import "CustomView.h"
#implementation CustomView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
//[self addSubview:titleLbl];
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(100, 100, 100, 50);
[button addTarget:self.delegate action:#selector(didButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"pressMe" forState:UIControlStateNormal];
[self addSubview:button];
}
return self;
}
in your ViewController.m
-(void)loadView
{
[super loadView];
CustomView *view = [[CustomView alloc]initWithFrame:self.view.bounds];
view.delegate = self;
[self.view addSubview:view];
}
This is what the responder chain was built for. When you add a target to your button, just supply nil for the target:
[mySpecialButton addTarget:nil
action:#selector(mySpecialButtonTapped:)
forControlEvents:UIControlEventTouchUpInside];
The nil target basically means "send mySpecialButtonTapped: to any object in the responder chain that can handle it".
Now you can handle this selector anywhere in the responder chain, which includes the button itself, its containing view, its containing view controller, the UIApplication, and finally your AppDelegate. Just place this method in the object most appropriate for your needs:
- (void)mySpecialButtonTapped:(id)sender {
NSLog("My special button was tapped!");
}
You don't need delegates or callback blocks (as in the accepted answer) if you just want to bubble a message up.
I guess that you expected something more fundamental then just pass some button action to controller.
I always follow MVC pattern in case of model/view/controller collaboration. It resolve your issue and many other. And I want to share my experience.
Separate controller from view and model: don't put all of the "business logic" into view-related classes; this makes the code very unusable. Make controller classes to host this code, but ensure that the controller classes don't make too many assumptions about the presentation.
Define callback APIs with #protocol, using #optional if not all the methods are required.
For view define protocol like <view class name>Protocol (example NewsViewProtocol). For controller define delegate like <view class name>Delegate (example NewsViewDelegate) and dataSource like <view class name>DataSource (example NewsViewDataSource). Keep all this #protocols in one separate file named <view class name>Protocol.h (example NewsViewProtocol.h)
Short example:
Contents of NewsView.h
//
// NewsView.h
#interface NewsView : UIView <NewsViewProtocol> {
#protected
NSObject* delegate_;
NSObject* dataSource_;
}
#end
Contents of NewsController.h and .m
//
// NewsController.h
#interface NewsController : UIViewController <NewsViewDataSource, NewsViewDelegate> {
}
#property (nonatomic, weak) UIView<NewsViewProtocol>* customView;
#end
#implementation NewsController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView = (UIView<NewsViewProtocol>*)self.view;
[self.customView setDelegate:self];
[self.customView setDataSource:self];
}
#end
Contents of NewsViewProtocol.h
//
// NewsViewProtocol.h
#protocol NewsViewProtocol;
#protocol NewsViewDelegate<NSObject>
#optional
- (void)someAction;
- (void)newsView:(UIView<NewsViewProtocol>*)newsView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
#end
#protocol NewsViewDataSource<NSObject>
#required
- (id)newsView:(UIView<NewsViewProtocol>*)newsView itemAtIndexPath:(NSIndexPath *)indexPath;
- (NSInteger)numberOfItemsInNewsView:(UIView<NewsViewProtocol>*)newsView section:(NSInteger)section;
- (BOOL)newsView:(UIView<NewsViewProtocol>*)newsView shouldDisplaySection:(NSInteger)section;
#end
#protocol NewsViewProtocol<NSObject>
#required
//Never retain delegate instance into implementation of this method
- (void)setDelegate:(NSObject<NewsViewDelegate>*)delegate;
//Never retain delegate instance into implementation of this method
- (void)setDataSource:(NSObject<NewsViewDataSource>*)dataSource;
- (void)reload;
#end
You may consider that it is redundant. In simple view controller, YES. But if you develop very complex screen with huge amount of data then it gives you some advantages as:
Helps you to separate responsibility between view and controller.
Keeps your code clear.
Makes you code more reusable.
Life is easy in xCode.
At the very beginning be sure that your xib View (the one with your button inside it) is associated to the right ViewController class. Which can be the default ViewController class that comes with a new project or your custom one.
After this, here comes the magic trick! Separate your view into 2 panel. The goal is to see your xib and your viewController code (the .m file). Now press the control key of your keyboard and drag your UIButton to the code. Select IBAction. It will generate something you can call a "listener" in other language. Go to the core code of your View Controller and complete the method!
Easy as that! Have fun :)
You don't really need delegates for this - it is how UIButtons are intended to be used. Just control-click and drag from your button to the .m file for your UIViewController. This will create a new method. From there, you can either make a call to the method you wrote or just copy-paste what you have into the new method.
You can try this:
[yourButton addTarget:self action:#selector(yourButtonAction:) forControlEvents:UIControlEventTouchUpInside];
And in your selector specify the action
- (IBAction)yourButtonAction:(id)sender {
//Action to perform
}
To add a button programmatically, in myViewController.m
UIView *yourView = [[UIView alloc] init];
UIButton *yourButton = [[UIButton alloc] initWithFrame:CGRectMake(0,0,100,21)];
[yourButton addTarget:self action:#selector(yourMethod) forControlEvents:UIControlEventTouchDown];
[yourView addSubview:yourButton];
More info here.

Send a Delegate message from UIPopover to Main UIViewController

I'm trying to use a Button in my UIPopover to create a UITextView in my Main UIViewController the code I have looks something like this (PopoverView.h file):
#protocol PopoverDelegate <NSObject>
- (void)buttonAPressed;
#end
#interface PopoverView : UIViewController <UITextViewDelegate> { //<UITextViewDelegate>
id <PopoverDelegate> delegate;
BOOL sendDelegateMessages;
}
#property (nonatomic, retain) id delegate;
#property (nonatomic) BOOL sendDelegateMessages;
#end
Then in my PopoverView.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton * addTB1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
addTB1.frame = CGRectMake(0, 0, 100, 50);
[addTB1 setTitle:#"Textbox One" forState:UIControlStateNormal];
[self.view addSubview:addTB1]; // Do any additional setup after loading the view from its nib.
[addTB1 addTarget:self action:#selector(buttonAPressed)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)buttonAPressed
{
NSLog(#"tapped button one");
if (sendDelegateMessages)
[delegate buttonAPressed];
}
And also in my MainViewController.m :
- (void)buttonAPressed {
NSLog(#"Button Pressed");
UITextView *textfield = [[UITextView alloc] init];
textfield.frame = CGRectMake(50, 30, 100, 100);
textfield.backgroundColor = [UIColor blueColor];
[self.view addSubview:textfield];
}
I'm using a delegate protocol to link the popover and the ViewController but I'm stuck on how I get my BOOL statement to link the -(void)buttonAPressed in the PopoverView and MainViewController so that when I press the button in the Popover a textview appears in the Main VC. How would I go about doing this?
In MainViewController, where you create PopoverView, be sure to set its delegate property otherwise sending messages to delegate in PopoverView will do nothing.
For example, in MainViewController.m:
PopoverView *pov = [[PopoverView alloc] initWithNibName:nil bundle:nil];
pov.delegate = self; // <-- must set this
thePopoverController = [[UIPopoverController alloc] initWithContent...
I am not sure why you need the sendDelegateMessages variable. Even with that bool, you must set the delegate property so PopoverView has an actual object reference to send the messages to.
If you want to make sure the delegate object has implemented the method you're about to call, you can do this instead:
if ([delegate respondsToSelector:#selector(buttonAPressed)])
[delegate buttonAPressed];
Also, the delegate property should be declared using assign (or weak if using ARC) instead of retain (see Why use weak pointer for delegation? for an explanation):
#property (nonatomic, assign) id<PopoverDelegate> delegate;
Another thing is if you're not using ARC, you need to add [textfield release]; at the end of the buttonAPressed method in MainViewController to avoid a memory leak.

UIButton deallocated issue

I received this message from the debugger console :
-[UIButton release]: message sent to deallocated instance 0x1836b0
But I don't create UIButton programmatically, all my buttons have created in Interface Builder. Each of them are linked to a function, like this :
-(IBAction)theFunction:(UIButton *)sender;
In lot of this functions, I don't use the variable sender. I don't even try to release it. So I don't understand why my application try to release my buttons.
Do I do something in Interface Builder to release or not my UIButton? Is it about the picture I put in the UIButton? If I use the variable (UIButton *)sender, do I need to release it?
This problem stucks me because of it my application crashes.
Edit:
- (IBAction)showPopoverOverview:(UIButton *)sender {
TouchPlanePopover *content = [[TouchPlanePopover alloc] init];
[content setTheAlbum:#"Overview"];
// Setup the popover for use in the detail view.
detailViewPopover = [[UIPopoverController alloc] initWithContentViewController:content];
detailViewPopover.popoverContentSize = CGSizeMake(600., 400.);
detailViewPopover.delegate = self; // Set the sender to a UIButton.
// Present the popover from the button that was tapped in the detail view.
[detailViewPopover presentPopoverFromRect:sender.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
// Set the last button tapped to the current button that was tapped.
lastTappedButton = sender;
[content release];
}
detailViewPopover is create in .h like this : #property (nonatomic, retain) UIPopoverController *detailViewPopover;
Thans for your help
Let me know if you need more information, I will edit the post
Change
-(IBAction)theFunction:(UIButton *)sender
to
-(IBAction)theFunction:(id)sender
Just try...
I am working on this and its work properly
So may be its useful for you.
myController.h
#import <\UIKit/UIKit.h>
#class myController;
#interface myController : UIViewController {
}
- (IBAction)onButtonClick:(UIButton *)button;
#end
myController.m
#implementation myController
- (IBAction)onButtonClick:(UIButton *)button {
NSLog(#"work properly");
}

Resources