I create custom login page by using UIAlerView subclass. Now when i click on button it opens up UIAlertView I want to change the main view based on which button is pressed.
But as all implementation of UIAlerView is in another class though i change the view it doesn't retain that as that class variable doesn't get it's value.
Can anyone please help me with this? I can post the code if required.
Thank you,
Ankita
You can use a custom init method like below for alertView and store the _sender in global or class variable. like
id sender;
- (id)initWithSender:(id)_sender
{
self = [super init];
if (self) {
sender=_sender;
}
return self;
}
from RootVC/bgview initialize alertView as follows and define a method named
-(void) alertIndexSelected:(NSInterger) index;
{
//change the backgound view based on button selected
}
in rootvc/your main view.
alertViewobj =[[alertView alloc] initWithSender:self];
when the button is selected on alertview call the below method, this will notify your rootvc about which index of alert is pressed. use following alertview delegate.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
[sender alertIndexSelected: buttonIndex];
}
If you are planning to use delegate methods then I think you need to referrer to some of these links.
How to use custom delegates in Objective-C
How do I create delegates in Objective-C?
http://iosdevelopertips.com/objective-c/the-basics-of-protocols-and-delegates.html
I hope this will help you a great deal in understanding delegates.
Related
I am working with the library KYCircleMenu. You can find it over here. I am also working with storyboards. I made a Class MenuViewController that is derived from KYCircleMenu
#interface MenuViewController : KYCircleMenu
Next I have implemented my initWithCoder like this.
- (id)initWithCoder:(NSCoder*)aDecoder
{
NSLog(#"called");
if(self = [self initWithButtonCount:kKYCCircleMenuButtonsCount
menuSize:kKYCircleMenuSize
buttonSize:kKYCircleMenuButtonSize
buttonImageNameFormat:kKYICircleMenuButtonImageNameFormat
centerButtonSize:kKYCircleMenuCenterButtonSize
centerButtonImageName:kKYICircleMenuCenterButton
centerButtonBackgroundImageName:kKYICircleMenuCenterButtonBackground])
{
}
return self;
}
And finally I have implemented a method from the KyCicrleMenu RunButtonActions. This method tells me what button is pressed in the menu. So in this method I am trying to do a segue to another viewcontroller. I am doing it like this.
NSLog(#"tag is %d",[sender tag]);
[self performSegueWithIdentifier:#"showNews" sender:self];
(The log gives me the button tag from the button that is pressed).
For some reason or another I keep getting this error.
Receiver (<MenuViewController: 0x1cd7cf50>) has no segue with identifier 'showNews''
Here is a screenshot from my storyboard.
Can anybody help me with this annoying problem?
Kind regards
- (id)initWithCoder:(NSCoder*)aDecoder
{
NSLog(#"called");
if(self = [self initWithButtonCount:kKYCCircleMenuButtonsCount
menuSize:kKYCircleMenuSize
buttonSize:kKYCircleMenuButtonSize
buttonImageNameFormat:kKYICircleMenuButtonImageNameFormat
centerButtonSize:kKYCircleMenuCenterButtonSize
centerButtonImageName:kKYICircleMenuCenterButton
centerButtonBackgroundImageName:kKYICircleMenuCenterButtonBackground])
{
}
return self;
}
Here, you are doing nothing with the aDecoder object - this contains all of the information from the storyboard (including the segue). Instead you are creating a brand new object, ignoring anything you have set up in the storyboard.
I've had a quick look at the repository and it doesn't seem to be tailored towards use in a storyboard - it implements its own loadView method, it has a designated initialiser and so on. You'd have to play around with it to set those properties after calling [super initWithCoder:aDecoder];, perhaps by pulling out the setup code from the designated initialiser and putting it into a separate method.
I have an iPhone app that has a tableview which contains cells that when touched show a detail of that object. I would like to add a Delete button to the bottom of the detail view. When the user presses it the object which is represented by the cell should be removed and the app should return to the TableView.
In terms of best practices, which is the ideal way to accomplish this?
There are few ways in which you can signal the deletion. One of them is delegates. You can define your delegate like this,
#protocol DetailViewControllerDelegate
- (void)shouldDeleteDetailObject:(id)object
#end
And then your table view controller subclass adopt the protocol and implement the method like,
- (void)shouldDeleteDetailObject:(id)object {
[self.objectsArray removeObject:object];
[self.navigationController popViewControllerAnimated:YES];
}
And then you message [self.tableView reloadData]; in viewWillAppear: as sandy has indicated.
Your button action will be implemented as,
- (IBAction)deleteObject {
if ( self.delegate && [self.delegate respondsToSelector:#selector(shouldDeleteDetailObject:)] ) {
[self.delegate shouldDeleteDetailObject:self.detailObject];
}
}
And delegate should be an assigned property.
You can also look at notifications but this is a better route for this situation.
I think there is nothing serious about this, if you successfully delete the particular details after that on backing on previous view (tableview) you just use this
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
In my UIViewController I have a UINavigationController with a default back button. When the user clicks the back button, a warning message should appear: "Do you really want to go back?". I know, that it is not possible to trap the back button event. It's only possible the use viewWillDisappear and set a flag:
- (void)viewWillDisappear:(BOOL)animated {
if (backBtnPressed) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Question" message:#"Do you really want to go back?" delegate:self cancelButtonTitle:#"No" otherButtonTitles: #"Yes", nil] autorelease];
[alert show];
}
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
// don't go back!
// cancel the back button event
}
else if (buttonIndex == 1) {
// go back
}
}
But with this code I have no chance! I can't stop the back button event, isn't it?
Do I have to write my own back button and set it as leftBarButtonItem? Or is there anybody with a great idea? :-)
Thanks for your help!
My answer from another thread matches this question. So I repost it here:
I've implemented UIViewController-BackButtonHandler extension. It does not need to subclass anything, just put it into your project and override navigationShouldPopOnBackButton method in UIViewController class:
-(BOOL) navigationShouldPopOnBackButton {
if(needsShowConfirmation) {
// Show confirmation alert
// ...
return NO; // Ignore 'Back' button this time
}
return YES; // Process 'Back' button click and Pop view controller
}
Download sample app.
What you need to do is to use the delegate of the navigation bar, and not the navigation controller.
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item; // called to push. return NO not to.
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item; // called at end of animation of push or immediately if not animated
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item; // same as push methods
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;
viewWillDisappear is a delegate method for the event that the view is going to disappear - and there's nothing the developer can do about that! If you could, it would be a viewShouldDisappear delegate method.
So I guess the only way is as you suggest, to use a custom leftBarButtonItem.
I must say this is one of the common use cases that Apple doesn't seem to make easy, and I see a lot of effort trying to get this working. I thought maybe I should summarize my findings here.
As many have pointed out, the method below in UINavigationBarDelegate is key to implementing this feature.
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
Many have subclassed UINavigationController and implemented the method above to make it easy to use without direct access to the UINavigationBar.
Unfortunately, there still remain some issues.
The swipe back gesture does not invoke this method.
Although it seems necessary, crashes are reported calling popViewControllerAnimated: in this method.
The Back button remains grayed out, when pop is cancelled.
Swipe back gesture
We need to intercept the gesture by setting the delegate as is done in https://stackoverflow.com/a/23173035/2400328 .
If the UINavigationController is subclassed, that would be:
self.interactivePopGestureRecognizer.delegate = self
and implementing:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
Take care in when you modify the delegate property, as it gets modified after the initializer is called.
Not calling popViewControllerAnimated:
Although undocumented, calling popViewControllerAnimated: can be avoided as in https://stackoverflow.com/a/26084150/2400328.
It involves calling navigationBar:shouldPopItem: of UINavigationController (from the subclass).
The Back button
Although this may be a minor detail (especially, if you have designed your own Back button), there is a simple solution (written by me :) https://stackoverflow.com/a/29440633/2400328
You only need to set a property YES and NO.
auto item = navigationBar.topItem;
item.hidesBackButton = YES;
item.hidesBackButton = NO;
You can use a custom button with a graphics, which looks exactly like "Back" button and create a custom leftBarButtonItem view as UIButton with this graphics. Add target self to your button with custom back: selector and pop your alert there. If the user presses "yes" to quit dismiss this view controller, if not - do nothing. The trick here is the button which looks exactly as navigation bar's back button.
Its better if u make your own back button and make it the left button of the Navigation controller. That can definitely help u to perform any action
If you're looking for a way to do this in Swift on iOS 10, you can create a custom UINavigationController and then a UINavigationBarDelegate extension:
class MyNavigationController : UINavigationController {
}
extension MyNavigationController : UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return false
}
}
The Method
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
is doing what you want. Unfortunately we are not supposed to delegate UINavigationBar to our own objects :(
The Apple Documentation states :
... In addition, a navigation controller object automatically assigns itself as the delegate of its UINavigationBar object and prevents other objects from changing that relationship. ...
One/The? way to do what you want is to put in your own back-button.
In that Method you do your tests and call
[self.navigationController popViewControllerAnimated:true];
if the user is allowed to go back.
I have one delegate file, one View Controller and one UIPopoverController
My PopoverController is defined in delegate file.
when I click on the view controller's button, the popover is shown.
The view controller also contains one method named "refreshviewnow".
When I click on the PopoverController "submit button", I want to call that refreshviewnow function.
What should I have to write on submit button event ?
You need to make sure and set the delegate property of the PopoverController to the View Controller. Then in the "submit button" you can invoke the method on the delegate.
- (void) submit {
[delegate refreshAction];
}
Good Luck.
I am not sure what you mean by "submit button" (maybe you should post some of the code you are using), but if you are trying to call a method that is in the same controller, you would do it like this:
- (void) methodToDisplayPopover
{
[self refreshViewNow];
}
EDIT:
MySecondView *second = [[MySecondView alloc] init];
[second refreshViewNow];
[second release];
I got the answer now.
add observer in View Controller file for method refreshviewnow.
post the notification when popover is going to dismiss.
I have a button inside the content of a UIPopoverController. This button runs a method called myAction.
MyAction has the form
- (void) myAction:(id)sender
so, myAction receives the id of the caller button.
Now, inside this method I would like to dismiss the UIPopoverController, but the only thing I have is the ID of the caller button. Remember that the button is inside the UIPopoverController.
Is there a way to discover the ID of the UIPopoverController, given the button ID I already have?
thanks.
Unfortunately no. At least, not within the standard practices. You might be able to travel up the responder stack to find it, but it's a hack, it's buggy, and it's really, really messy.
If you want to dismiss a popover by pushing a button, some place relevant should keep a reference to the popover. Usually that would be the owner of the popover (not the controller showed within the popover). When the button is pressed, it can send a message to the owner controller, which can then dismiss the popover.
You might be tempted to have the controller displayed inside of the popover be the owner of its own popover, but coding this way is brittle, can get messy (again), and may result in retain loops so that neither ever gets released.
You can access the presenting popoverController by accessing "popoverController" with KVC.
[[self valueForKey:#"popoverController"] dismissPopoverAnimated:YES]
I have this working, and I do not think it is a hack. I have a standard split view iPad app. I then added a method on my detail controller (the owner of the pop over) to handle the dismissal.
On the standard split view architechture, both the root and detail view controllers are available via the app delegate. So I bound a button click inside the pop over to call a method which gets the app delegate. From there I call the method on the detail controller to dismiss the pop over.
This is the code for the method on the View Controller that is displayed inside the popover:
- (void) exitView: (id)sender {
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.detailViewController exitDrill];
}
Then the simple method to dismiss on the Detail View Controller:
- (void) exitDrill {
if(dtController != nil){
[dtController dismissPopoverAnimated: YES];
[dtController release];
}
}
I like the ability to do this because it give me a way to show a user how they can exit a pop over. This may not be necessary in future versions of the app; for right now, while this paradigm is still new to the platform, I prefer to let the users gexit a display in a couple fo different ways to make sure I minimize frustration.
As Ed Marty already wrote
If you want to dismiss a popover by pushing a button, some place relevant should keep a reference to the popover
This is very true; however, when showing a UIPopoverController, the class opening the popovercontroller keeps this resource already. So, what you could do is to use this class as the delegate class for your Popover Controller.
To do so, you could do the following, which I use in my code.
In the class opening the popover, this is my code:
- (void)showInformationForView:(Booking*)booking frame:(CGRect)rect
{
BookingDetailsViewController *bookingView = [[BookingDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped booking:booking];
[bookingView setDelegate:self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:bookingView];
self.popController = [[UIPopoverController alloc] initWithContentViewController:navController];
[self.popController setDelegate:self];
[self.popController setPopoverContentSize:CGSizeMake(320, 320)];
rect.size.width = 0;
[self.popController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
- (void)dismissPopoverAnimated:(BOOL)animated
{
[self.popController dismissPopoverAnimated:animated];
}
So what I am doing here is creating a UINavigationController and setting a BookingDetailsViewController as its rootViewController. Then I am also adding the current class as delegate to this BookingDetailsViewController.
The second thing I added is a dismissal method called dismissPopoverAnimated:animated.
In my BookingDetailsViewController.h I added the following code:
[...]
#property (nonatomic, strong) id delegate;
[...]
And in my BookingDetailsViewController.m I added this code:
[...]
#synthesize delegate = _delegate;
- (void)viewDidLoad
{
UIBarButtonItem *closeButton = [[UIBarButtonItem alloc] initWithTitle:#"Close" style:UIBarButtonItemStylePlain target:self action:#selector(closeView)];
[self.navigationItem setRightBarButtonItem:closeButton];
[super viewDidLoad];
}
- (void)closeView
{
if ([self.delegate respondsToSelector:#selector(dismissPopoverAnimated:)]) {
[self.delegate dismissPopoverAnimated:YES];
}
else {
NSLog(#"Cannot close the view, nu such dismiss method");
}
}
[...]
What happens is that when the "Close" button in the UINavigationController is pressed, the method closeView is called. This method check if the delegate responds to dismissPopoverAnimated:animated and if so, it calls it. If it does not respond to this method it will show a log message and do nothing more (so it wont crash).
I have written my code using ARC, hence there is no memory management.
I hope this helped you.