My view controller I represent modally never get reallocates it is always living (saw by count in instruments).
I saw similar questions on this site, the answers were "you should find what strong object is pointing to the modal view controller"
I cannot find it out, please help me
here is my code:
here I call for modal view:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismissKeyboard];
[self CheckAndStore:indexPath.row];
NSLog(#"At Select");
self->DetailsView = [[InfoViewController alloc]initWithParameter:[PersonsFromSearch objectAtIndex:indexPath.row]];
DetailsView.delegate = self;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[self presentViewController:DetailsView animated:YES completion:nil];
}
here inside modal view:
-(void) Back
{
NSLog(#"Back Clicked");
[self dismissViewControllerAnimated:NO completion:nil];
}
initWithParameter:
- (id) initWithParameter:(id)parameter
{
Dict = parameter;
return self;
}
inside detailsView.h
#interface InfoViewController : UIViewController <UITableViewDelegate, UITableViewDataSource , UISearchBarDelegate
,MFMessageComposeViewControllerDelegate,MFMailComposeViewControllerDelegate>
{
id<InfoViewControllerDelegate> __weak delegate;
NSDictionary *Dict;
…
}
#property (weak, nonatomic) id <InfoViewControllerDelegate> delegate;
...
DetailsView is an iVar (could it be a problem ?)
If need more code please tell me I will post (I am quite newbie in iOS development).
You should declare your delegate in DetailsView to be weak to avoid strong reference cycle. Also you need to set your self->DetailsView to nil when you want to release it. Because if you still hold a strong reference to it, how could it be deallocated?
Related
Already manage to pass input text from first VC to second VC with tableview.
First VC do not have tableview.
First VC:
UItextField - user type some name.
UIButton *add - button with segue (prepareForSegue)
Second VC:
TableView displaying input text from first VC with prepareForSegue
Question:
Tableview displays only one row at the time, so when i click back to input another name, and click add buton, tableview obviously gets reset and does not remember first input text. So how get tableview to remember names and put it in other rows. I don't know should i type code in prepareForSegue, or make delegate in first VC. Please explain in detail. Thank you alot.
I believe there are many ways to achieve this, if you want to persist data maybe core Data is your best bet, however if its just a simple logic then I suggest you using delegates.
ViewController
Interface
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *dataTextField;
#property (nonatomic) NSMutableArray *items;
- (IBAction)AddData:(id)sender;
#end
Implementation
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_items = [[NSMutableArray alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)AddData:(id)sender {
if ([self.dataTextField.text length]> 0) {
[_items addObject:self.dataTextField.text];
[self performSegueWithIdentifier:#"tableSegue" sender:self];
}else{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Error"
message:#"You must enter some data"
delegate:self
cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
}
}
- (void)addItemViewController:(TableViewController *)controller didFinishSelectingItem:(NSMutableArray *)item selectedTag:(int)tag{
NSLog(#"DATA=%#", item);
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"tableSegue"]){
TableViewController *controller = (TableViewController *)segue.destinationViewController;
controller.items = _items;
}
}
#end
TableViewController
Interface:
#protocol TableViewControllerDelegate <NSObject>
- (void)addItemViewController:(id)controller didFinishSelectingItem:(NSMutableArray *)item selectedTag:(int)tag;
#end
#interface TableViewController : UITableViewController
#property (nonatomic, weak) id <TableViewControllerDelegate> delegate;
#property (nonatomic) NSMutableArray *items;
#end
Implementation
#implementation TableViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.navigationController popViewControllerAnimated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
[[cell textLabel] setText:_items[indexPath.row]];
return cell;
}
#end
View controllers are part of the controller layer of your application. They do not do "business logic" — heavy processing or more-persistent storage of data, whether to disk or just for that session.
The model section of your application handles that. Each view controller gets and sets data via the model. There should be no ongoing conversations between view controllers*; anything beyond things you would specify to an init is indicative of a broken design.
So you're asking the wrong question.
You would have a model that somehow vends the items that should go into the first view controller. You will have a second view controller that knows how to edit one item. The level of communication from first to second will be "this is the item you should be editing".
It is the responsibility of the first view controller and the model to ensure that it can keep its display up to date. The second view controller is responsible only for modifying its record. It shouldn't need to communicate anything whatsoever to the first view controller.
Whether you do that by pulling results from the model on every viewWillAppear, by some sort of live observation, by notifications emanating outward from the model or by some other means entirely doesn't matter.
(* subject to caveats where you've used containment, e.g. changes to the title that a view controller has but which is shown by a navigation controller are technically an ongoing conversation)
I have subclassed a view that I am using as header view it has some buttons delegate inside it and it works perfect .
However I am presenting a modalViewController above my viewController .
(in my modalViewController I have implemented the same header , and it does get the delegates from the header) but this view it self has to delegate to the previous viewController if the back button of the header is pressed.
I have made the same functions but my viewController never gets it's delegate... :(
I am quite new to Obj-C and I don't know maybe I am doing something illegal here.
here is the code of modalViewController trying to delegate to the previous viewController
#pragma mark - header delegate
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
//delete the items array
//_itemSourceArray = nil;
[delegate allEventsDrillPage:self backbuttonPressed:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
This delegate does triggers from the header view.
however in my previous viewController:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender //doesn't work :(
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// [self dismissViewControllerAnimated:YES completion:nil];
_drillPage = nil;
}
never get called
I calling the modalViewController like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[_allEventsTableView deselectRowAtIndexPath:indexPath animated:YES];
[self->_allEventsTableView setNeedsDisplay];
[self->_allEventsTableView reloadData];
_drillPage = [[allEventsDrillPage alloc]initWithDictionary:((NSDictionary*) [_tableDataSource objectAtIndex:indexPath.row])];
_drillPage.delegate = self;
[self presentViewController:_drillPage animated:YES completion:nil];
}
in its .h file I did
#interface allEvents : UIViewController <headerDelegate , UITableViewDataSource , UITableViewDelegate ,allEventsDrillPageDelegate>
I don't get what am I missing here :-/ can some1 take a look please ?
if needed more info I will added just ask for it.
EDIT:
protocol of delegate inside the modalViewController
#class allEventsDrillPage;
#protocol allEventsDrillPageDelegate //define delegate protocol
- (void)allEventsDrillPage:(allEventsDrillPage*)allEventsDrillPage backbuttonPressed:(UIButton*)sender;
#end
#interface allEventsDrillPage : UIViewController
{
id<allEventsDrillPageDelegate> __weak delegate;
....
}
#property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate; //define
Let's try:
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
// delete the items array
//_itemSourceArray = nil;
// my comment: you should replace "delegate" to "_delegate"
// and it works. I tested. It's OK. Wow.
[_delegate allEventsDrillPage:self backbuttonPressed:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
nmh's answer is correct. I wrote up an answer earlier but his came in faster, so I thought I would take out the other parts and just add the explanations here.
You have this:
#interface allEventsDrillPage : UIViewController
{
id<allEventsDrillPageDelegate> __weak delegate;
....
}
#property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate;
With this id<allEventsDrillPageDelegate> __weak delegate; you declare an ivar.
With this #property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate; you are declaring a property.
Since Xcode 4.4 you get auto-synthesization.
And so this line:
_drillPage.delegate = self;
You are setting the one via the property.
And not this:
id<allEventsDrillPageDelegate> __weak delegate;
So what you have here:
[delegate allEventsDrillPage:self backbuttonPressed:sender];
You are using the ivar above, not the one via the property.
And delegate is thus nil.
And so, if you try to send a message to the delegate using nmh's solution or:
[self.delegate allEventsDrillPage:self backbuttonPressed:sender];
It should work as expected.
Addendum to dismissing modal view controller:
Instead of dismissing it from the current view controller, dismiss it from the one who presented it, so:
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
[_delegate allEventsDrillPage:self backbuttonPressed:sender];
//or self.delegate
}
}
And in:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
[self dismissViewControllerAnimated:YES completion:nil];
_drillPage = nil;
}
Or even better:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
[self dismissViewControllerAnimated:YES completion:^{
_drillPage = nil;
}];
}
Hope this helps.
My app's storyboard looks like this.
There is a list of websites defined in an array in the PageViewController. What I'm now trying to do is to let the user type the index number in the textfield in the MainViewController and when you tap the button, in the page view controller, display the corresponding website from the array.Below is my code (I know there are flaws in this app. This is just a demo),
PageViewController.m
#import "MainViewController.h"
#import "PageViewController.h"
#interface MainViewController ()
#property (nonatomic, assign) NSInteger selectedIndex;
#end
#implementation MainViewController
- (IBAction)buttonPressed:(id)sender
{
self.selectedIndex = [self.textField.text integerValue];
[self performSegueWithIdentifier:#"ToNext" sender:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
PageViewController *pageVC = [segue destinationViewController];
pageVC.index = self.selectedIndex;
}
#end
The selectedIndex property gets the number the user types in correctly. The problem arises when it's passed on to the pageVC.
I have another property called index of NSInteger type in the PageViewController to retrieve the passed in value. The problem is it always shows 0.
Can anybody tell me what I'm missing here? I'd really appreciate any suggestion.
I have also uploaded a project demonstrating my issue here in case if my question isn't too clear.
In your code remove :
- (void)awakeFromNib
{
[super awakeFromNib];
[self createPages];
self.dataSource = self;
NSLog(#"Index = %lu", self.index);
[self setViewControllers:[NSArray arrayWithObject:self.pages[self.index]] direction:UIPageViewControllerNavigationDirectionForward animated:true completion:nil];
}
and replace it by :
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self createPages];
self.dataSource = self;
NSLog(#"Index = %d", self.index);
[self setViewControllers:[NSArray arrayWithObject:self.pages[self.index]] direction:UIPageViewControllerNavigationDirectionForward animated:true completion:nil];
}
I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...
I have a UITableView in a popover. When a user selects a row in the popover I am wanting it to close the popover and save some data in the cell to a variable in the parent view controller. What is the most efficient way to do this?
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
In this method you handle the user selection. For passing variables between view controllers from a UITableView to another ViewController read this great tutorial from ray wenderlich : http://www.raywenderlich.com/1797/how-to-create-a-simple-iphone-app-tutorial-part-1
Create a delegate in popover's table view controller and pass the variable to it as cell data
In .h of the popover's table view controller
#protocol PopoverTableViewControllerDelegate <NSObject>
- (void)didSelectRow:(NSString *)cellDataString;
#end
#interface PopoverTableViewController : UITableViewController
#property (strong, nonatomic) id<PopoverTableViewControllerDelegate> delegate;
#end
In the .m's didSelectRowAtIndexPath call the delegate and pass the cell data variable as
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate didSelectRow:cellDataString];
}
- (void)dealloc
{
[super dealloc];
[_delegate release];
}
Implement it in parent view controller in .h implement the PopoverTableViewControllerDelegate as
#interface ParentViewController : UIViewController <PopoverTableViewControllerDelegate>
#property (strong, nonatomic) NSString *cellDataString;
#end
and in .m implement the delegate as
- (void)dealloc
{
[super dealloc];
[_cellDataString release];
}
PopoverTableViewController *popoverTableViewController = [[[PopoverTableViewController alloc] init] autorelease];
popoverTableViewController.delegate = self;
- (void)didSelectRow:(NSString *)cellDataString
{
self.cellDataString = cellDataString;
[popOverController dismissPopoverAnimated:YES];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ParentViewController *parent = [[ParentViewController alloc] initWithNibName:#"ParentViewController" bundle:nil];
parent.variable = //do something you want
[self.navigationcontroller pushViewController:parent animated:YES];
}
Something like this... Hope this helps...