dismissViewControllerAnimated does not deallocate viewcontroller - ios

First off: My project is ARC enabled and I'm using storyboard.
I have a view controller that pushes a segue (modal),
[self performSegueWithIdentifier: #"goInitialSettings" sender: self];
there i'm setting some parameters and store them. When the parameters are stored (true a button tap), the app should return to the original viewcontroller.
This i am doing with this command:
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{}];
I'm noticing that the viewcontroller that i dismiss, never deallocs. How does this come?
I'm adding the code of the 'presented viewcontroller' below:
#interface CenterChoiceController ()
{
UIView* _titleBackground;
UILabel* _lblTitle;
UIButton* _btnGaVerder;
UIPickerView* _myPickerView;
NSArray* _centers;
UILabel* _adresLine;
UILabel* _cityLine;
MKPointAnnotation* _point;
MKMapView* _mapView;
UIActivityIndicatorView* _indicator;
UIAlertView* _alert;
GCenter* _center;
DataManager* _dm;
}
#end
#implementation CenterChoiceController
-(void)dealloc
{
NSLog(#"Centerchoice deallocs");
_titleBackground = nil;
_lblTitle = nil;
_btnGaVerder = nil;
_myPickerView = nil;
_point = nil;
_mapView = nil;
_indicator = nil;
_alert = nil;
_centers = nil;
_adresLine = nil;
_cityLine = nil;
_center = nil;
_dm = nil;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_dm = [[DataManager alloc]init];
if([_dm hasConnectivity])
{
[_dm fetchCentersForController:self];
}
else
{
[self pushErrorMessage:NSLocalizedString(#"nointernetconnection", nil)];
}
CAGradientLayer *bgLayer = [BackgroundLayer blueGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
_titleBackground = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
_titleBackground.backgroundColor = [GColor blueColor];
[self.view addSubview:_titleBackground];
_lblTitle = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width - 10, 44)];
_lblTitle.textAlignment = NSTextAlignmentRight;
_lblTitle.textColor = [GColor whiteColor];
_lblTitle.text = NSLocalizedString(#"bioscoopkeuze", nil);
[self.view addSubview:_lblTitle];
_btnGaVerder = [[UIButton alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 54, self.view.frame.size.width, 54)];
[_btnGaVerder setTitle:NSLocalizedString(#"gaverder", nil) forState:UIControlStateNormal];
_btnGaVerder.titleLabel.font = [_btnGaVerder.titleLabel.font fontWithSize:12];
_btnGaVerder.backgroundColor = [GColor blueColor];
[_btnGaVerder setTitleColor:[GColor whiteColor] forState:UIControlStateNormal];
[_btnGaVerder setShowsTouchWhenHighlighted:YES];
[_btnGaVerder addTarget:self action:#selector(gaVerder) forControlEvents:UIControlEventTouchUpInside];
_myPickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 44, self.view.frame.size.width, 200)];
}
-(void)showLoading
{
NSLog(#"shows loading");
_indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
CGPoint cntr = self.view.center;
_indicator.center = cntr;
[_indicator startAnimating];
[self.view addSubview:_indicator];
}
-(void)hideLoading
{
NSLog(#"hides loading");
[_indicator removeFromSuperview];
_indicator = nil;
}
-(void)pushData:(NSArray *)data
{
[self.view addSubview:_btnGaVerder];
[self.view addSubview:_myPickerView];
_centers = data;
_myPickerView.delegate = self;
_myPickerView.dataSource = self;
_dm = [[DataManager alloc]init];
GSettings* settings = [_dm loadSettings];
if(settings == nil)
{
settings = [[GSettings alloc]init];
settings.chosenCenter = [_centers objectAtIndex:0];
settings.loadedCenter = [_centers objectAtIndex:0];
_center = settings.chosenCenter;
settings.notificationsEnabled = YES;
[self changeAddressLines];
}
/*if(settings != nil)
{
GCenter* loaded = settings.loadedCenter;
int i = 0;
BOOL found = NO;
while(i < [_centers count] && !found)
{
GCenter* center = (GCenter*)[_centers objectAtIndex:i];
if(settings.loadedCenter.iD == center.iD)
{
_center = center;
settings.chosenCenter = center;
[_dm storeSettings:settings];
found = YES;
}
i++;
}
//[self.myPickerView selectRow:i-1 inComponent:0 animated:NO];
loaded = nil;
[self changeAddressLines];
}
*/
}
-(void) pushErrorMessage: (NSString*) errorMessage
{
_alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"fout", nil) message:errorMessage delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
_alert.delegate = self;
[_alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 0)
{
if(self.navigationController != nil)
{
[self.navigationController popViewControllerAnimated:YES];
}
else
{
//[self initializeData];
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)viewWillDisappear:(BOOL)animated
{
[_dm cancelCenterRequest];
/*if(self.tabBarController != nil)
{
dm = [[DataManager alloc]init];
settings = [dm loadSettings];
if([dm hasConnectivity])
{
settings.lastUpdated = nil;
[dm storeSettings:settings];
}
if(settings.loadedCenter.centerCode != settings.chosenCenter.centerCode)
{
UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
SplashScreenController *controller = [mystoryboard instantiateViewControllerWithIdentifier:#"root"];
[self presentViewController:controller animated:YES completion:nil];
}
dm = nil;
settings = nil;
}
*/
}
-(void)gaVerder
{
_dm = [[DataManager alloc]init];
GSettings* settings = [_dm loadSettings];
if(settings == nil)
{
settings = [[GSettings alloc]init];
settings.notificationsEnabled = YES;
}
if(_center != nil)
{
settings.chosenCenter = _center;
}
[_dm storeSettings:settings];
[_mapView removeFromSuperview];
_mapView = nil;
_titleBackground = nil;
_lblTitle = nil;
_btnGaVerder = nil;
_myPickerView = nil;
_point = nil;
_indicator = nil;
_alert = nil;
_centers = nil;
_adresLine = nil;
_cityLine = nil;
_center = nil;
_dm = nil;
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{}];
//DEZE BLIJFT HELAAS IN HET GEHEUGEN HANGEN... GEEN OPLOSSING GEVONDEN
//[self.navigationController popViewControllerAnimated:NO];
}
//PICKERVIEWDELEGATE EN DATASOURCE
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [_centers count];
}
- (UILabel *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
GCenter* center = (GCenter*)[_centers objectAtIndex:row];
NSString* string = center.name;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, pickerView.frame.size.width, 44)];
label.textColor = [GColor blueColor];
label.font = [label.font fontWithSize:18];
label.text = string;
label.textAlignment = NSTextAlignmentCenter;
return label;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
_center = (GCenter*)[_centers objectAtIndex:row];
[self changeAddressLines];
}
-(void)changeAddressLines
{
if (_mapView != nil)
{
[_mapView removeAnnotation:_point];
}
[_adresLine removeFromSuperview];
[_cityLine removeFromSuperview];
_adresLine = nil;
_cityLine = nil;
CGRect rctAdres = CGRectMake(0, _myPickerView.frame.origin.y + _myPickerView.frame.size.height -10, self.view.frame.size.width, 20);
_adresLine = [[UILabel alloc]initWithFrame:rctAdres];
_adresLine.textAlignment = NSTextAlignmentCenter;
_adresLine.textColor = [GColor greyColor];
_adresLine.text = _center.street;
CGRect rctCity = CGRectMake(0, rctAdres.origin.y + rctAdres.size.height, self.view.frame.size.width, 20);
_cityLine = [[UILabel alloc]initWithFrame:rctCity];
_cityLine.textAlignment = NSTextAlignmentCenter;
_cityLine.textColor = [GColor greyColor];
_cityLine.font = [_cityLine.font fontWithSize:14];
_cityLine.text = _center.city;
[self.view addSubview:_adresLine];
[self.view addSubview:_cityLine];
if(_mapView == nil)
{
double height;
height = _btnGaVerder.frame.origin.y - _cityLine.frame.origin.y - _cityLine.frame.size.height;
CGRect mapRect = CGRectMake(0, _cityLine.frame.origin.y+3 + _cityLine.frame.size.height, self.view.frame.size.width, height);
_mapView = [[MKMapView alloc]initWithFrame:mapRect];
[self.view addSubview:_mapView];
}
CLLocationCoordinate2D punt;
punt.latitude = _center.latitude;
punt.longitude = _center.longitude;
_point = [[MKPointAnnotation alloc] init];
[_point setCoordinate:punt];
_mapView.centerCoordinate = punt;
_point.title = _center.name;
[_mapView addAnnotation:_point];
[_mapView setCenterCoordinate:punt animated:YES];
MKCoordinateRegion theRegion = _mapView.region;
theRegion.span.longitudeDelta = 0.005;
theRegion.span.latitudeDelta = 0.005;
[_mapView setRegion:theRegion animated:YES];
}
#end

In my case it was a little more complicated. I don't have any variable that has strong reference to my view controller, and my view controller is not a strong delegate to any property/variable contained inside this class itself. After some hard thinking and trials, I found my issue was caused by a NSTimer object defined in the interface. The timer object itself is non-repeatable, but the method invoked by it will schedule the timer again at the end, which as you can imagine would reference this method defined in my view controller again, thus causing circular references. To break out of this loop, I had to invalidate the timer before I dismiss my view controller.
As a summary, these are cases when a view controller can be blocked from deallocating after it is dismissed:
The view controller is being strongly referenced by some outside object;
The view controller is a strong delegate referenced by some object defined within the view controller itself
The dismissViewControllerAnimated:completion: block may reference to self or it has some other code block that may cause a circular references
The view controller has NSTimer objects which can invoke some methods which re-schedules the timer
There could be more, but hopefully we can capture a lot of cases with the above cases.

If your view controller is not deallocated after it is dismissed, there's probably a strong reference to that view controller somewhere in your code. ARC will always deallocate objects that doesn't have strong reference anymore.

Related

Using only one tool from CLImageEditor

I am working on adding text on image. I found that CLImageEditor tool is very good. but i only want to use add text tool with my own theme. so can i use that tool in my app. or any other tool that i can use in my app.
You could do this for every tool.
CLImageToolInfo *tool = [editor.toolInfo subToolInfoWithToolName:#"CLToneCurveTool" recursive:NO];
tool.available = NO; // if available is set to NO, it is removed from the menu view.
No, you cant directly open a submenu. May be raise an issue to them at https://github.com/yackle/CLImageEditor/issues
If you want to show only few options or you want to customize item location you can do this way:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
CLImageEditor *editor = [[CLImageEditor alloc] initWithImage:image];
editor.delegate = self;
CLImageToolInfo *tool1 = [editor.toolInfo subToolInfoWithToolName:#"CLAdjustmentTool" recursive:NO];
CLImageToolInfo *tool2 = [editor.toolInfo subToolInfoWithToolName:#"CLBlurTool" recursive:NO];
CLImageToolInfo *tool3 = [editor.toolInfo subToolInfoWithToolName:#"CLRotateTool" recursive:NO];
CLImageToolInfo *tool4 = [editor.toolInfo subToolInfoWithToolName:#"CLToneCurveTool" recursive:NO];
tool1.available = NO;
tool2.available = NO;
tool3.available = NO;
tool4.available = NO;
[picker pushViewController:editor animated:YES];
}
This is possible by tweeking the existing code as below:
//First disable all the tools like below but the one you need.
var tool = editor.toolInfo.subToolInfoWithToolName("CLRotateTool", recursive: false)
tool.available = false
//replace below 4 functions in CLImageEditorView Controller
- (void)setMenuView
{
CGFloat x = 0;
CGFloat W = 70;
CGFloat H = _menuView.height;
int toolCount = 0;
CGFloat padding = 0;
for(CLImageToolInfo *info in self.toolInfo.sortedSubtools){
if(info.available){
toolCount++;
}
}
CGFloat diff = _menuView.frame.size.width - toolCount * W;
if (0<diff && diff<2*W) {
padding = diff/(toolCount+1);
}
for(CLImageToolInfo *info in self.toolInfo.sortedSubtools){
if(!info.available){
continue;
}
CLToolbarMenuItem *view = [CLImageEditorTheme menuItemWithFrame:CGRectMake(x+padding, 0, W, H) target:self action:#selector(tappedMenuView:) toolInfo:info];
[_menuView addSubview:view];
x += W+padding;
[self tappedMenuView:view];
}
_menuView.contentSize = CGSizeMake(MAX(x, _menuView.frame.size.width+1), 0);
}
- (IBAction)pushedCancelBtn:(id)sender
{
_imageView.image = _originalImage;
[self resetImageViewFrame];
self.currentTool = nil;
[self pushedCloseBtn:nil];
}
- (IBAction)pushedDoneBtn:(id)sender
{
self.view.userInteractionEnabled = NO;
[self.currentTool executeWithCompletionBlock:^(UIImage *image, NSError *error, NSDictionary *userInfo) {
if(error){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
else if(image){
_originalImage = image;
_imageView.image = image;
[self resetImageViewFrame];
self.currentTool = nil;
}
self.view.userInteractionEnabled = YES;
[self pushedFinishBtn:nil];
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = self.toolInfo.title;
self.view.clipsToBounds = YES;
self.view.backgroundColor = self.theme.backgroundColor;
self.navigationController.view.backgroundColor = self.view.backgroundColor;
if([self respondsToSelector:#selector(automaticallyAdjustsScrollViewInsets)]){
self.automaticallyAdjustsScrollViewInsets = NO;
}
if([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]){
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
[self initNavigationBar];
[self initMenuScrollView];
[self initImageScrollView];
if(_imageView==nil){
_imageView = [UIImageView new];
[_scrollView addSubview:_imageView];
[self refreshImageView];
}
[self setMenuView];
}
You can not open submenu directly.
I created a remover function for removing the subtools. Swift 4.2
private func removeTools(tools: [String]) {
tools.forEach {
guard let imageEditor = self.imageEditor else { return }
let tool = imageEditor.toolInfo.subToolInfo(withToolName: $0, recursive: false)
tool?.available = false
}
}
You can call like this;
removeTools(tools: ["CLFilterTool", "CLEffectTool"])

Loading overlay on collection view

I have a loading view that I insert over the top of a view while it is parsing data from the internet using a separate thread, in this case its on top of a UICollectionView.
For some reason that I cannot understand the loading overlay disappears on its own whist the parsing is still taking place, presenting a blank screen for a second or two. It doesn't seem to happen on UITableViews just on UIcollectionviews. Any help would be appreciated.
Activity overlay.m:
#import "ActivityOverlayController.h
#interface ActivityOverlayController ()
#end
#implementation ActivityOverlayController
-(id)initWithFrame:(CGRect)theFrame {
if (self = [super init]) {
frame = theFrame;
self.view.frame = theFrame;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", #"ActivityOverlayController called");
container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 110, 30)];
activityLabel = [[UILabel alloc] init];
activityLabel.text = NSLocalizedString(#"Loading", #"string1");
activityLabel.textColor = [UIColor lightGrayColor];
activityLabel.font = [UIFont boldSystemFontOfSize:17];
[container addSubview:activityLabel];
activityLabel.frame = CGRectMake(0, 3, 70, 25);
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.color = [UIColor blackColor];
activityIndicator.hidesWhenStopped = TRUE;
[activityIndicator startAnimating];
activityIndicator.frame = CGRectMake(80, 0, 30, 30);
[container addSubview:activityIndicator];
[self.view addSubview:container];
container.center = CGPointMake(frame.size.width/2, frame.size.height/2);
self.view.backgroundColor = [UIColor colorWithRed:255 green:255 blue:255 alpha:0.7];
}
-(void)viewWillAppear:(BOOL) animated {
[super viewWillAppear:animated];
}
-(void)viewWillDisappear:(BOOL) animated {
[super viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[activityIndicator stopAnimating];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Parsing part of collectionViewController.m:
-(void)ParseCollectionView{
[self performSelectorOnMainThread:#selector(showActivityView) withObject:nil waitUntilDone:YES];
CollectionViewImages = [[NSMutableArray alloc]init];
NSData *xmlData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:GalleryTitle]];
cvTBXML = [[TBXML alloc]initWithXMLData:xmlData];
// Obtain root element
TBXMLElement * root = cvTBXML.rootXMLElement;
TBXMLElement * channel = [TBXML childElementNamed:#"channel" parentElement:root];
if (root)
{
TBXMLElement * item = [TBXML childElementNamed:#"item" parentElement:channel];
while (item !=nil)
{
//create new instance of news object
NSObject *Imagetoparse = [[GalleryImage alloc] init];
TBXMLElement * link = [TBXML childElementNamed:#"link" parentElement:item];
NSString *linktext= [TBXML textForElement:link];
[Imagetoparse setValue:linktext forKey:#"link"];
TBXMLElement * thumbnail = [TBXML childElementNamed:#"description" parentElement:item];
NSString *Thumbnailtext= [TBXML textForElement:thumbnail];
Thumbnailtext = [Thumbnailtext substringFromIndex:21];
NSInteger *slength = Thumbnailtext.length -4;
Thumbnailtext = [Thumbnailtext substringToIndex:slength];
Thumbnailtext = [Thumbnailtext stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
Thumbnailtext = [Thumbnailtext stringByReplacingOccurrencesOfString:#"thumb" withString:#"main"];
NSLog(#"Thumnail1 : %#", Thumbnailtext);
NSData *Thumbnailimage = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: Thumbnailtext]];
[Imagetoparse setValue:Thumbnailimage forKey:#"thumbnail"];
[CollectionViewImages addObject:Imagetoparse];
item = [TBXML nextSiblingNamed:#"item" searchFromElement:item];
}
}
[self.collectionView reloadData];
NSLog(#"%#", #"Finished Parse Collection View");
[self performSelectorOnMainThread:#selector(hideActivityView) withObject:nil waitUntilDone:NO];
}
-(void)showActivityView {
if (overlayController == nil) {
overlayController = [[ActivityOverlayController alloc] initWithFrame:(self.view.superview.bounds)];
}
[self.view.superview insertSubview:overlayController.view aboveSubview:self.view];
}
-(void)hideActivityView {
[overlayController.view removeFromSuperview];
}

disabling multitouch not working ios

I have a view controller(VC) having 3 buttons(country , state , city). On click of these buttons , i am presenting another VC(using popover segue) , which is a search table to search country (or states or city). Now if user clicks two buttons , then both the view controller are presented at the same time. I want only one to be presented.How to do this?
Tried self.view.multipleTouchEnabled = NO; but not working.
code of the searchVC:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIImage *bgApplication = [UIImage imageNamed:#"bg-app-568h.jpg"];
self.view.backgroundColor = [UIColor colorWithPatternImage:bgApplication];
self.searchItems = [self loadData];
self.filteredSearchItems = [NSMutableArray arrayWithCapacity:[self.searchItems count]];
for (UIView *view in self.SearchBarBase.subviews){
if ([view isKindOfClass: [UITextField class]]) {
UITextField *tf = (UITextField *)view;
tf.delegate = self;
break;
}
}
}
- (NSArray *)loadData
{
MatchDayDataController * sharedController = [MatchDayDataController sharedDataController];
NSArray *data = [sharedController fetchStates];
//NSLog(#"states: %#", data);
return data;
}
Code of the presenter VC:
-(void) viewWillAppear:(BOOL)animated
{
keyboardIsShown = NO;
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
// Load match venue data
self.locationField.text = sharedController.matchVenue;
//Load home team related data
NSString *stateText = [sharedController.homeStateName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([stateText length] == 0 )
{
self.selectHomeAssoc.enabled = NO;
self.selectHomeClub.enabled = NO;
}
NSString *homeAssocText = [sharedController.homeAssociationName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([homeAssocText length] == 0 )
{
self.selectHomeAssoc.enabled = YES;
self.selectHomeClub.enabled = NO;
}
NSString *homeClubText = [self.homeClub.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([homeClubText length] == 0 )
{
self.selectHomeClub.enabled = YES;
}
self.stateTextFieldHome.text = sharedController.homeStateName;
self.homeAssociation.text = sharedController.homeAssociationName;
self.homeClub.text = sharedController.homeClubName;;
self.homeTeam.text = sharedController.homeTeamName;
// Away team related data
self.stateTextFieldAway.text = sharedController.awayStateName;
self.awayAssociation.text = sharedController.awayAssocationName;
self.awayClub.text = sharedController.awayClubName;
self.awayTeam.text = sharedController.awayTeamName;
}
- (void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.matchVenue = self.locationField.text;
// Save Venue related data
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yyyy HH:mm"];
NSString *dateTimeString = [self.dateField.text stringByAppendingString:#" "];
dateTimeString = [dateTimeString stringByAppendingString:self.timeField.text];
//NSLog(#"DateTimeString : %#", dateTimeString);
NSDate *matchDateTime = [dateFormatter dateFromString:dateTimeString];
sharedController.inspectionDate = matchDateTime;
//Save Home team related data
sharedController.homeTeamName = self.homeTeam.text;
// Save away team related data
sharedController.awayTeamName = self.awayTeam.text;
[sharedController saveData];
// hide the keyboard when we come back after leaving the cursor on text field.
// I have called resignFirstResponder on homeTeam Text field. You can use any of text field to hide.
[self.homeTeam resignFirstResponder];
[self.awayTeam resignFirstResponder];
}
- (void)viewDidDisappear:(BOOL)animated
{
[self.view endEditing:YES];
[super viewDidDisappear:animated];
}
// This event is called when the user clicks on Done/Next button in the key board.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)segue sender:(id)sender
{
if (![sender isKindOfClass:[UIBarButtonItem class]]) {
return true;
}
return [JLTValidator validateFields:#[self.locationField, self.dateField, self.timeField, self.homeTeam, self.homeClub, self.homeAssociation, self.stateTextFieldHome, self.stateTextFieldAway, self.awayAssociation, self.awayClub, self.awayTeam]];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//NSLog(#"segue.identifier : %#", segue.identifier);
self.popSegue = (UIStoryboardPopoverSegue *)segue;
if([segue.identifier isEqualToString:#"toSearchHomeStateFromButton"]
|| [segue.identifier isEqualToString:#"toSearchHomeStateFromText"] )
{
SearchHomeStateViewController *viewController = segue.destinationViewController;
viewController.delegate = (id) self;
}
else if ([segue.identifier isEqualToString:#"toSearchHomeAssocFromButton"] || [segue.identifier isEqualToString:#"toSearchHomeAssocFromText"])
{
HomeAssocSearchViewController *viewController = segue.destinationViewController;
viewController.delegate = (id) self;
}
else if ([segue.identifier isEqualToString:#"toSearchHomeClubFromButton"] || [segue.identifier isEqualToString:#"toSearchHomeClubFromText"])
{
SearchHomeClubViewController *viewController = segue.destinationViewController;
viewController.delegate = (id) self;
}
else if([segue.identifier isEqualToString:#"toSearchAwayStateFromButton"] || [segue.identifier isEqualToString:#"toSearchAwayStateFromText"])
{
StateViewController *viewController = segue.destinationViewController;
viewController.delegate = (id) self;
}
else if ([segue.identifier isEqualToString:#"toSearchAwayAssocFromButton"] || [segue.identifier isEqualToString:#"toSearchAwayAssocFromText"])
{
SearchAwayAssocViewController *viewController = segue.destinationViewController;
viewController.delegate = (id) self;
}
else if ([segue.identifier isEqualToString:#"toSearchAwayClubFromButton"] || [segue.identifier isEqualToString:#"toSearchAwayClubFromText"])
{
SearchAwayClubViewController *viewController = segue.destinationViewController;
viewController.delegate = (id) self;
}
}
-(void) searchHomeStateDone:(NSString *)selectedState
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.stateAwayTeam = sharedController.stateHomeTeam;
sharedController.homeTeamName = EMPTY_STRING;
sharedController.awayStateName = sharedController.homeStateName;
sharedController.awayAssocationName = EMPTY_STRING;
sharedController.awayAssociationId = EMPTY_STRING;
sharedController.awayClubName = EMPTY_STRING;
sharedController.awayClubId = EMPTY_STRING;
[self updateDataOnScreen];
self.selectHomeAssoc.enabled = YES;
self.selectHomeClub.enabled = NO;
[self.popSegue.popoverController dismissPopoverAnimated:YES];
}
-(void) searchHomeAssocDone:(NSString *)selectedHomeAssoc
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.awayAssocationName = sharedController.homeAssociationName;
sharedController.homeTeamName = EMPTY_STRING;
sharedController.awayStateName = sharedController.homeStateName;
sharedController.awayAssociationId = sharedController.homeAssociationId;
sharedController.awayClubName = EMPTY_STRING;
sharedController.awayClubId = EMPTY_STRING;
[self updateDataOnScreen];
self.selectHomeClub.enabled = YES;
[JLTValidator clearTextFieldValidation:self.homeAssociation];
[self.popSegue.popoverController dismissPopoverAnimated:YES];
}
-(void) searchHomeClubDone:(NSString *)selectedHomeClub
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.homeTeamName = EMPTY_STRING;
[self updateDataOnScreen];
[JLTValidator clearTextFieldValidation:self.homeClub];
[self.popSegue.popoverController dismissPopoverAnimated:YES];
}
-(void) searchAwayStateDone:(NSString *)selectedAwayState
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.homeTeamName = self.homeTeam.text;
[self updateDataOnScreen];
[JLTValidator clearTextFieldValidation:self.stateTextFieldAway];
[self.popSegue.popoverController dismissPopoverAnimated:YES];
}
-(void) searchAwayAssocDone:(NSString *)selectedAwayAssoc
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.homeTeamName = self.homeTeam.text;
[self updateDataOnScreen];
[JLTValidator clearTextFieldValidation:_awayAssociation];
[self.popSegue.popoverController dismissPopoverAnimated:YES];
}
-(void) searchAwayClubDone:(NSString *) selectedAwayClub;
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
sharedController.homeTeamName = self.homeTeam.text;
[self updateDataOnScreen];
[JLTValidator clearTextFieldValidation:_awayClub];
[self.popSegue.popoverController dismissPopoverAnimated:YES];
}
-(void) updateDataOnScreen
{
MatchDayDataController *sharedController = [MatchDayDataController sharedDataController];
// sharedController.awayTeamName = EMPTY_STRING;
self.stateTextFieldHome.text = sharedController.homeStateName;
self.homeAssociation.text =sharedController.homeAssociationName;
self.homeClub.text = sharedController.homeClubName;
self.homeTeam.text = sharedController.homeTeamName;
self.stateTextFieldAway.text = sharedController.awayStateName;
self.awayAssociation.text = sharedController.awayAssocationName;
self.awayClub.text = sharedController.awayClubName;
//self.awayTeam.text = sharedController.awayTeamName;
}
Any ideas ?
Thanks,
You can create static bool variable busy, and at first line of each button action method check out state of this variable. If busy equal to NO, then set her to YES and at last line of action method or somewhere else set her to NO. If busy equal to YES do return from action method. So, until first called method didn't finish no other methods will be running.
#implementation yourController
-(void)changeCountry
{
if (!busy)
{
busy = YES;
// do what you need
} else return;
}
-(void)changeState
{
if (!busy)
{
busy = YES;
// do what you need
} else return;
}
#end
You should implement singleton, which return a static instance with BOOL value for using him in difference viewControllers (1 mainC + 3 popOverC) and set busy to NO when you finish edition.
try self.view.exclusiveTouch = YES;

iOS NSFetchedResultsController: Is it possible to use same cache for the same fetch in different View Controllers?

I have two different view controllers displaying almost the same fetch request. One of them displays more info than the other and is also used for editing of the data. The second one is just for display.
Is there a way to speed things up, since both are displaying exactly the same data? I'm looking for something in my code which makes the whole reorder slow after displaying the second view controller for the first time. In other words: Initially the reordering in my main view controller is very fast. Then you just display the second view controller and switch back and then the reordering in the main view controller gets slow. I have reason to assume that it is because they are using the same fetch.
I'm initiating the fetch request in both viewDidLoad methods, as in the following code snippets. The first one is my main view controller, and also the first one displayed when starting the app:
- (void)setupFetchedResultsController
{
self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MainCategory"];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"position" ascending:YES]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:#"MainCategoryCache"];
}
And this is my second one:
- (void)setupFetchedResultsController
{
self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MainCategory"];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"position" ascending:YES]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:#"NetCache"];
}
Here are the view did load and cellForRowAtIndexPath of my main view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupFetchedResultsController];
//Edit/Done button
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Edit", nil) style:UIBarButtonItemStyleBordered target:self action:#selector(editTable:)];
[self.navigationItem setRightBarButtonItem:editButton];
//Budget at bottom
self.sumTitleLabel.text = [NSString stringWithFormat:#"%#%#:",NSLocalizedString(#"Budget", nil),NSLocalizedString(#"PerMonth", nil)];
self.sumLabel.text = [[DatabaseFetches budgetPerMonthForManagedObjectContext:self.managedObjectContext] getLocalizedCurrencyString];
//Layout
self.view.backgroundColor = [UIColor clearColor];
[self styleTableView];
[self styleBudgetTotal];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Data model and cell setup
static NSString *CellIdentifier = #"MainCategoryCell";
MainCategoryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MainCategory *mainCategory = [self.fetchedResultsController objectAtIndexPath:indexPath];
//Clear background color
cell.backgroundColor = [UIColor clearColor];
//Setup the cell texts
cell.title.text = mainCategory.name;
int numberOfSubcategories = [[mainCategory getNumberOfSpendingCategories] integerValue];
if (numberOfSubcategories == 1) {
cell.subcategories.text = [NSString stringWithFormat:#"%i %#", numberOfSubcategories, NSLocalizedString(#"subcategory", nil)];
} else {
cell.subcategories.text = [NSString stringWithFormat:#"%i %#", numberOfSubcategories, NSLocalizedString(#"subcategories", nil)];
}
cell.costs.text = [[mainCategory getMonthlyCostsOfAllSpendingCategories] getLocalizedCurrencyString];
//Delegation
cell.title.delegate = self;
//Format text
cell.title.font = [Theme tableCellTitleFont];
cell.title.textColor = [Theme tableCellTitleColor];
cell.title.tag = indexPath.row;
cell.subcategories.font = [Theme tableCellSubTitleFont];
cell.subcategories.textColor = [Theme tableCellSubTitleColor];
cell.costs.font = [Theme tableCellValueFont];
cell.costs.textColor = [Theme tableCellValueColor];
//Icon
UIImage *icon;
if(!mainCategory.icon){
icon = [UIImage imageNamed:#"DefaultIcon.png"];
} else {
icon = [UIImage imageNamed:mainCategory.icon];
}
[cell.iconButton setImage:icon forState: UIControlStateNormal];
cell.icon.image = icon;
cell.iconButton.tag = indexPath.row;
//Background image
cell.cellBackground.image = [[UIImage imageNamed:#"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
//Selection
//cell.title.highlightedTextColor = cell.title.textColor;
cell.subcategories.highlightedTextColor = cell.subcategories.textColor;
cell.costs.highlightedTextColor = cell.costs.textColor;
return cell;
}
Complete second view controller:
#interface NetIncomeViewController()
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
#end
#implementation NetIncomeViewController
#synthesize incomeTitleLabel = _incomeTitleLabel;
#synthesize incomeValueTextField = _incomeValueTextField;
#synthesize incomeBackground = _incomeBackground;
#synthesize incomeValueBackground = _incomeValueBackground;
#synthesize netIncomeTitleLabel = _netIncomeTitleLabel;
#synthesize netIncomeValueLabel = _netIncomeValueLabel;
#synthesize netIncomeBackground = _netIncomeBackground;
#synthesize netIncomeValueBackground = _netIncomeValueBackground;
#synthesize managedObjectContext = _managedObjectContext;
#pragma mark Initializer and view setup
- (void)setupFetchedResultsController
{
self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MainCategory"];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"position" ascending:YES]];
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
}
#pragma mark View lifecycle
- (void)awakeFromNib{
[super awakeFromNib];
//Title (necessary here because otherwise the tab bar would be only localized after first click
//on the respective tab bar and not from beginning
self.title = NSLocalizedString(#"Net Income", nil);
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateNetIncome];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupFetchedResultsController];
//Edit/Done button
UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Edit", nil) style:UIBarButtonItemStyleBordered target:self action:#selector(editIncome:)];
[self.navigationItem setRightBarButtonItem:editButton];
//Get or set and get the gross income
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSNumber *grossIncome = [defaults objectForKey:#"income"];
if (!grossIncome) {
[defaults setDouble:0 forKey:#"income"];
[defaults synchronize];
grossIncome = [defaults objectForKey:#"income"];
}
self.incomeValueTextField.enabled = NO;
self.incomeValueTextField.delegate = self;
self.incomeValueTextField.keyboardType = UIKeyboardTypeDecimalPad;
self.incomeValueTextField.text = [grossIncome getLocalizedCurrencyString];
[self styleTableViewAndIncome];
}
- (void)styleTableViewAndIncome
{
self.view.backgroundColor = [UIColor clearColor];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = [UIColor clearColor];
self.incomeTitleLabel.textColor = [Theme budgetValueTitleColor];
self.incomeTitleLabel.font = [Theme budgetValueTitleFont];
self.incomeValueTextField.textColor = [Theme budgetValueColor];
self.incomeValueTextField.font = [Theme budgetValueTitleFont];
self.netIncomeTitleLabel.textColor = [Theme budgetValueTitleColor];
self.netIncomeTitleLabel.font = [Theme budgetValueTitleFont];
self.netIncomeValueLabel.textColor = [Theme budgetValueColor];
self.netIncomeValueLabel.font = [Theme budgetValueFont];
self.incomeTitleLabel.text = [NSString stringWithFormat:#"%#:",NSLocalizedString(#"Income", nil)];
self.netIncomeTitleLabel.text = [NSString stringWithFormat:#"%#%#:",NSLocalizedString(#"Net", nil),NSLocalizedString(#"PerMonth", nil)];
self.incomeBackground.image = [[UIImage imageNamed:#"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
self.incomeValueBackground.image = [[UIImage imageNamed:#"price-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
self.netIncomeBackground.image = [[UIImage imageNamed:#"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
}
#pragma mark Table view delegate methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CategoryCostCell";
CategoryCostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor clearColor];
// Configure the cell layout
MainCategory *mainCategory = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Configure the cell layout
cell.categoryBackground.image = [[UIImage imageNamed:#"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
if(!mainCategory.icon){
cell.categoryImage.image = [UIImage imageNamed:#"DefaultIcon.png"];
} else {
cell.categoryImage.image = [UIImage imageNamed:mainCategory.icon];
}
cell.categoryName.text = mainCategory.name;
NSNumber *expenditures = [mainCategory getMonthlyCostsOfAllSpendingCategories];
double expendituresDouble =-[expenditures doubleValue];
if (expendituresDouble == 0) {
expenditures = [NSNumber numberWithDouble: 0];
} else {
expenditures = [NSNumber numberWithDouble: expendituresDouble];
}
cell.categoryCosts.text = [expenditures getLocalizedCurrencyString];
//Format the text
cell.categoryName.font = [Theme tableCellSmallTitleFont];
cell.categoryName.textColor = [Theme tableCellSmallTitleColor];
cell.categoryCosts.font = [Theme tableCellSmallValueFont];
cell.categoryCosts.textColor = [Theme tableCellSmallValueColor];
return cell;
}
#pragma mark TextField delegate methods
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if(self.editing){
return YES;
} else {
return NO;
}
}
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
textField.text = [NSString stringWithFormat:#"%.2f",[NSNumber getUnLocalizedCurrencyDoubleWithString:textField.text]];
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
textField.text = [[NSNumber numberWithDouble:[textField.text doubleValue]] getLocalizedCurrencyString];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//Handle the enter button
[textField resignFirstResponder];
[self endEdit];
//Since this method is called AFTER DidEndEditing, we have to call editTable or in this case since we have put all the endEdit code in a seperate method this method directly in order to format the table back from edit mode to normal.
return YES;
}
#pragma mark Edit/Add/Delete action
- (IBAction) editIncome:(id)sender
{
if(self.editing)
{
[self endEdit];
} else {
[self beginEdit];
}
}
- (void) beginEdit
{
//Change to editing mode
[super setEditing:YES animated:YES];
//Exchange the edit button with done button
[self.navigationItem.rightBarButtonItem setTitle:NSLocalizedString(#"Done", nil)];
[self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStyleDone];
self.incomeValueTextField.borderStyle = UITextBorderStyleRoundedRect;
self.incomeValueTextField.enabled = YES;
self.incomeValueBackground.hidden = YES;
self.incomeValueTextField.textColor = [Theme budgetValueTitleColor];
}
- (void) endEdit
{
//Change to editing no
[super setEditing:NO animated:YES];
//Remove Done button and exchange it with edit button
[self.navigationItem.rightBarButtonItem setTitle:NSLocalizedString(#"Edit", nil)];
[self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStylePlain];
self.incomeValueTextField.borderStyle = UITextBorderStyleNone;
self.incomeValueTextField.enabled = NO;
self.incomeValueBackground.hidden = NO;
self.incomeValueTextField.textColor = [Theme budgetValueColor];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setDouble:[NSNumber getUnLocalizedCurrencyDoubleWithString:self.incomeValueTextField.text] forKey:#"income"];
[self updateNetIncome];
//[defaults synchronize];
}
#pragma mark Net income
- (void) updateNetIncome {
double grossIncome = [NSNumber getUnLocalizedCurrencyDoubleWithString:self.incomeValueTextField.text];
double budget = [[DatabaseFetches budgetPerMonthForManagedObjectContext:self.managedObjectContext] doubleValue];
double netIncome = grossIncome - budget;
if(netIncome >0){
self.netIncomeValueBackground.image = [[UIImage imageNamed:#"price-bkg_green"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
} else if(netIncome <0) {
self.netIncomeValueBackground.image = [[UIImage imageNamed:#"price-bkg_red"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
} else {
self.netIncomeValueBackground.image = [[UIImage imageNamed:#"price-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
}
self.netIncomeValueLabel.text = [[NSNumber numberWithDouble:netIncome] getLocalizedCurrencyString];
}
#end
It looks like you're doing UIImage creation in your cellForRowAtIndexPath method, which would cause a slowdown.
Better to create the background UIImage as an instance variables and set its image in the viewDidLoad, then reference it in the cellForRowAtIndexPath method. Like so:
//Declare the instance variable
UIImage *backgroundImage
//Set it in viewDidLoad
self.backgroundImage = [[UIImage imageNamed:#"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
//Assign it in cellForRowAtIndexPath
cell.backgroundImage = self.backgroundImage;
As for the icon, which has to be set for each row, you'll want to move the actual image loading off the main thread. You can do this either with dispatch_async() or NSOperationQueue. (pulled from here: Understanding dispatch_async)
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
UIImage *icon;
if(!mainCategory.icon){
icon = [UIImage imageNamed:#"DefaultIcon.png"];
} else {
icon = [UIImage imageNamed:mainCategory.icon];
}
dispatch_async(dispatch_get_main_queue(), ^(void){
[cell.iconButton setImage:icon forState: UIControlStateNormal];
cell.icon.image = icon;
cell.iconButton.tag = indexPath.row;
});
});
Notice how the imageNamed method is called in the background thread, but the actual assignment takes place on the main thread. You have to do it this way because you're not allowed to update UI elements anywhere except the main thread.

[NSURL initFileURLWithPath:]: nil string parameter when trying to load a view

When my view is trying to load, the applications crashes and i get this stack:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter'
This is my code, i don't really know where to look when this crash is occurred:
#import "Itineraire.h"
#import "SBMapView.h"
#import "SBRouteAnnotation.h"
#import "City.h"
#import "UICGRoutes.h"
#import "SBCheckPointViewController.h"
//#import "SBRouteDetailView.h"
#interface Itineraire(Private)
-(void)customInitialization;
#end
#implementation Itineraire(Private)
-(void)customInitialization
{
// do the initialization of class variables here..
mDirections = [UICGDirections sharedDirections];
mDirections.delegate = self;
}
#end
#implementation Itineraire
#synthesize map = mMap;
#synthesize startPoint = mStartPoint;
#synthesize endPoint = mEndPoint;
#synthesize loadBtn = mLoadBtn;
#synthesize annotationArray = mAnnotationArray;
#synthesize travelMode = mTravelMode;
#synthesize destination;
#synthesize routes;
#synthesize mAnnotations;
#synthesize mRouteArray;
#synthesize mRouteDetail;
//Invoked when the class is instantiated in XIB
-(id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
if( self)
{
[self customInitialization];
}
return self;
}
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
[self customInitialization];
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Google Maps";
self.map = [[SBMapView alloc] initWithFrame:CGRectMake(0, 49, self.view.frame.size.width, self.view.frame.size.height)];
//self.map = [[SBMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 381)];
[self.view addSubview:mMap];
self.view.backgroundColor = [UIColor blackColor];
self.annotationArray = [[NSMutableArray alloc]init];
self.routes = [[UICGRoutes alloc]init];
if (mDirections.isInitialized) {
[self updateRoute];
}
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
}
- (void)viewWillDisappear:(BOOL)animated;
{
[super viewWillDisappear:YES];
}
#pragma mark -
#pragma mark Instance Methods
- (void)updateRoute
{ /*
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
UICGDirectionsOptions *options = [[[UICGDirectionsOptions alloc] init] autorelease];
options.travelMode = mTravelMode;
City *mFirstCity = [[[City alloc]init] autorelease];
mFirstCity.mCityName = #"Paris";//mStartPoint;
//[mDirections loadWithStartPoint:mFirstCity.mCityName endPoint:destination options:options];
//added
NSMutableArray * DestinationCityArray = [[NSMutableArray alloc]init];
[DestinationCityArray addObject:#"Berlin"];
[mDirections loadWithStartPoint:mFirstCity.mCityName endPoint:destination options:options];
*/
//
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
UICGDirectionsOptions *options = [[UICGDirectionsOptions alloc] init] ;
options.travelMode = mTravelMode;
City *mFirstCity = [[City alloc]init];
mFirstCity.mCityName = #"Amsterdam";//mStartPoint;
NSMutableArray *dest=[[NSMutableArray alloc]init];
[dest addObject:#"Berlin"];
[mDirections loadWithStartPoint:mFirstCity.mCityName endPoint:dest options:options];
}
-(void)loadRouteAnnotations
{
self.mRouteArray = [mDirections routeArray];
NSLog(#"mRouteArray %#",mRouteArray);
self.mAnnotations = [[NSMutableArray alloc]init];
for (int idx = 0; idx < [mRouteArray count]; idx++) {
NSArray *_routeWayPoints1 = [[mRouteArray objectAtIndex:idx] wayPoints];
NSArray *mPlacetitles = [[mRouteArray objectAtIndex:idx] mPlaceTitle];
self.annotationArray = [NSMutableArray arrayWithCapacity:[_routeWayPoints1 count]-2];
mLoadBtn.title = #"OFF";
mLoadBtn.target = self;
mLoadBtn.action = #selector(removeRouteAnnotations);
for(int idx = 0; idx < [_routeWayPoints1 count]-1; idx++)
{
mBetweenAnnotation = [[SBRouteAnnotation alloc] initWithCoordinate:[[_routeWayPoints1 objectAtIndex:idx]coordinate]
title:[mPlacetitles objectAtIndex:idx]
annotationType:SBRouteAnnotationTypeWayPoint];
[self.annotationArray addObject:mBetweenAnnotation];
}
[mAnnotations addObject:mAnnotationArray];
[self.map.mapView addAnnotations:[mAnnotations objectAtIndex:idx]];
NSLog(#"map %#",mMap);
}
}
-(void)showCheckpoints
{
SBCheckPointViewController *_Controller = [[SBCheckPointViewController alloc]initWithNibName:#"SBCheckPoints" bundle:nil];
[self.navigationController pushViewController:_Controller animated:YES];
NSMutableArray *arr = [[mDirections checkPoint] mPlaceTitle];
_Controller.mCheckPoints = arr ;
}
//
-(void)removeRouteAnnotations
{
NSMutableArray *mTempAnnotation;// = [mAnnotations retain];
for (int idx = 0; idx < [mTempAnnotation count]; idx++) {
[mMap.mapView removeAnnotations:[mTempAnnotation objectAtIndex:idx] ];
}
mLoadBtn.title = #"ON";
mLoadBtn.target = self;
mLoadBtn.action = #selector(loadRouteAnnotations);
}
#pragma mark <UICGDirectionsDelegate> Methods
- (void)directionsDidFinishInitialize:(UICGDirections *)directions {
[self updateRoute];
}
- (void)directions:(UICGDirections *)directions didFailInitializeWithError:(NSError *)error {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Map Directions" message:[error localizedFailureReason] delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertView show];
}
- (void)directionsDidUpdateDirections:(UICGDirections *)indirections {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UICGPolyline *polyline = [indirections polyline];
NSArray *routePoints = [polyline routePoints];
[mMap loadRoutes:routePoints]; // Loads route by getting the array of all coordinates in the route.
UIToolbar *tools = [[UIToolbar alloc]
initWithFrame:CGRectMake(0.0f, 0.0f, 103.0f, 44.01f)]; // 44.01 shifts it up 1px for some reason
tools.clearsContextBeforeDrawing = NO;
tools.clipsToBounds = NO;
tools.tintColor = [UIColor colorWithWhite:0.305f alpha:0.0f]; // closest I could get by eye to black, translucent style.
// anyone know how to get it perfect?
tools.barStyle = -1; // clear background
NSMutableArray *buttons = [[NSMutableArray alloc] initWithCapacity:2];
// Create a standard Load button.
self.loadBtn = [[UIBarButtonItem alloc]initWithTitle:#"ON"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(loadRouteAnnotations)];
[buttons addObject:mLoadBtn];
// Add Go button.
UIBarButtonItem *mGoBtn = [[UIBarButtonItem alloc] initWithTitle:#"Go"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showCheckpoints)];
[buttons addObject:mGoBtn];
// Add buttons to toolbar and toolbar to nav bar.
[tools setItems:buttons animated:NO];
UIBarButtonItem *twoButtons = [[UIBarButtonItem alloc] initWithCustomView:tools];
self.navigationItem.rightBarButtonItem = twoButtons;
//Add annotations of different colors based on initial and final places.
SBRouteAnnotation *startAnnotation = [[SBRouteAnnotation alloc] initWithCoordinate:[[routePoints objectAtIndex:0] coordinate]
title:mStartPoint
annotationType:SBRouteAnnotationTypeStart];
SBRouteAnnotation *endAnnotation = [[SBRouteAnnotation alloc] initWithCoordinate:[[routePoints lastObject] coordinate]
title:mEndPoint
annotationType:SBRouteAnnotationTypeEnd];
[mMap.mapView addAnnotations:[NSArray arrayWithObjects:startAnnotation, endAnnotation,nil]];
}
- (void)directions:(UICGDirections *)directions didFailWithMessage:(NSString *)message {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Map Directions" message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertView show];
}
#pragma mark -
#end
I tried to put my code on a new project (empty application) and it worked fine, i didn't understand that error and its possible causes.
EDIT:
I tried to track the problem by elimination,and my code worked fine when i remove this method:
//Invoked when the class is instantiated in XIB
-(id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
if( self)
{
[self customInitialization];
}
return self;
}
However, when i run, my app doesn't display me the route between the two points (The purpose of the app). So this method seems important for the whole class because without it, i couldn't get the route and in the other side when it's there, the app crashes. How should i deal with this contradiction?
#Vince is putting you on the right track. Since the problem is in an NSURL method being called by a framework (Google Maps in this case) you need to debug what you are passing to the framework. You have the source for the framework also right? You can set breakpoints in loadWithStartPoint:endPoint:options:] to see what's going on.
One thing I did notice is you are passing an NSMutableArray as the endPoint param when I believe it expects an NSString.

Resources