NSMutable Array crashed when being accessed at other method - ios

-(void)Aray
{
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
if(Counter < NewColor)
{
[ColorArray addObject:[NSNumber numberWithInteger:ColorTemp]];
Counter += 1;
}
}
-(IBAction)Go:(id)sender
{
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
Color = [[ColorArray objectAtIndex:Index] intValue];
if(Color == 2)
{
ColorLabel.text = #"The Color is Black";
Screen.image = [UIImage imageNamed:#"BlackTile.png"];
}
else
{
Screen.image = [UIImage imageNamed:#"Tunnel.png"];
ColorLabel.text = #"The Color is Green";
}
Index += 1;
}
-(IBAction)Black:(id)sender
{
ColorTemp = 2;
NewColor += 1;
[self Array];
}
-(IBAction)Green:(id)sender
{
ColorTemp = 1;
NewColor += 1;
[self Array];
}

The issue is that the ColorArray needs to be an instance variable (or #property) of the class so that it persists outside of the method calls.
This code will always crash, regardless of the value of Index:
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
Color = [[ColorArray objectAtIndex:Index] intValue];
Color appears to already be an instance variable (or #property), so this concept should not be alien to you.
Side note: variables conventionally start with lower case and use camal-case naming.

Related

How to remove data from an array comparing using string not an index in objective-C

I have an array of dictionary like below
self->arrField: (
{
fieldname = "Have you ever had any one of the following:";
fieldvalue = (
Diabetes,
Hypertension
);
tagvalue = 0;
},
{
fieldname = "By continuing with this survey, you agree to our terms of use and Parsons Information Governance and Privacy Policies, Procedures and Standards, available on the Corporate Policy Center.";
fieldvalue = Agree;
tagvalue = 1;
},
{
fieldname = "Have you tested positive for COVID-19 in the last 10 days?";
fieldvalue = No;
tagvalue = 4;
},
{
fieldname = "Do you have any known heart conditions or on any medications that elevate your heart rate";
fieldvalue = Yes;
tagvalue = 6;
},
{
fieldname = "Have you received a COVID-19 test in the past 48 hours";
fieldvalue = Yes;
tagvalue = 7;
},
{
fieldname = "In the last 48 hours have you experienced any of these symptoms";
fieldvalue = (
cough,
fever
);
tagvalue = 10;
}
)
So if the user changes any of the fieldvalue I need to replace that array, I have tried using replaceObjectAtIndex but it will not work out because I need compare the string.
Below is my implementation:
-(void)ChkUnChk:(UIButton*)sender {
dispatch_async(dispatch_get_main_queue(), ^{
UIButton *btn=(UIButton *)sender;
NSString *strValue = btn.accessibilityHint;
NSString *Str=[NSString stringWithFormat:#"%d",(int)btn.tag];
int iTag = [[sender.layer valueForKey:#"anyKey"] intValue];
// new Implementation
NSMutableDictionary *dictFields = [[NSMutableDictionary alloc] init];
NSMutableArray *arrFieldvalue = [[NSMutableArray alloc] init];
[dictFields setObject:[[self->dictCustomFieldGroups valueForKey:#"name"] objectAtIndex:iTag] forKey:#"fieldname"];
[dictFields setObject:[NSString stringWithFormat:#"%ld",(long)iTag] forKey:#"tagvalue"];
if ([self isTagIDAvailable:iTag]) {
arrFieldvalue = [[[self->arrTempCheckboxFields objectAtIndex:self->checkboxIndex] valueForKey:#"fieldvalue"] mutableCopy];
BOOL isTheObjectThere = [arrFieldvalue containsObject: strValue];
if (isTheObjectThere) {
NSLog(#"string contains");
[btn setBackgroundImage:[UIImage systemImageNamed:#"square"] forState:UIControlStateNormal];
for(id item in arrFieldvalue) {
if([item isEqual:strValue]) {
[arrFieldvalue removeObject:item];
[dictFields setObject:arrFieldvalue forKey:#"fieldvalue"];
break;
}
}
} else {
NSLog(#"string does not contain!");
[btn setBackgroundImage:[UIImage systemImageNamed:#"checkmark.square"] forState:UIControlStateNormal];
[arrFieldvalue addObject:strValue];
[dictFields setObject:arrFieldvalue forKey:#"fieldvalue"];
}
NSInteger count = [self->arrFields count];
for (NSInteger index = (count - 1); index >= 0; index--) {
NSArray *p = self->arrFields[index];
if ([[self->arrFields valueForKey:#"tagvalue"] isEqual:[NSNumber numberWithInt:iTag]]) {
[self->arrFields removeObjectAtIndex:index];
}
}
[self->arrFields replaceObjectAtIndex:iTag withObject:dictFields];
[self->arrTempCheckboxFields replaceObjectAtIndex:self->checkboxIndex withObject:dictFields];
} else {
[btn setBackgroundImage:[UIImage systemImageNamed:#"checkmark.square"] forState:UIControlStateNormal];
[arrFieldvalue addObject:strValue];
[dictFields setObject:arrFieldvalue forKey:#"fieldvalue"];
[self->arrFields addObject:dictFields];
[self->arrTempCheckboxFields addObject:dictFields];
}
NSLog(#"self->arrField: %#",self->arrFields);
});
}
-(BOOL) isTagIDAvailable:(int)tagID {
BOOL retVal = FALSE;
BOOL tagIdFound = FALSE;
if ([arrTagID count] > 0) {
for (int i = 0; i < [arrTagID count]; i++) {
if ([[NSNumber numberWithInteger:tagID] isEqualToNumber:[arrTagID objectAtIndex:i]]) {
tagIdFound = TRUE;
checkboxIndex = i;
retVal = TRUE;
break;
}
}
if (!tagIdFound) {
[arrTagID addObject:[NSNumber numberWithInteger:tagID]];
checkboxIndex = (int)[arrTagID count];
retVal = FALSE;
}
} else {
[arrTagID addObject:[NSNumber numberWithInteger:tagID]];
retVal = FALSE;
}
return retVal;
}
I have done something from line no 32, like I will remove that array value and add it again.But I am not sure how to do that. If more detail require I can provide.
Thanks in Advance.

ios-charts radar chart turn auto scaling OFF

These two radar charts have the same data points for the orange bit, but different for the gray. However, as you can see it is autoscaling, and the orange bit ends up bigger on the second chart, even though they are the same exact data points. Is there a way to turn autoscaling off? I would like 100% to go right to the edge of the chart also.
-(void)setupRadarChart
{
self.radarChart.descriptionText = #"";
self.radarChart.webLineWidth = .75;
self.radarChart.innerWebLineWidth = 0.375;
self.radarChart.webAlpha = 1.0;
self.radarChart.userInteractionEnabled = NO;
self.radarChart.xAxis.enabled = YES;
ChartYAxis *yAxis = self.radarChart.yAxis;
yAxis.enabled = NO;
ChartLegend *l = self.radarChart.legend;
l.position = ChartLegendPositionBelowChartLeft;
l.font = [UIFont systemFontOfSize:UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? 12 : (headerWidth/40)];
[self setRadarData];
}
- (void)setRadarData
{
NSMutableArray *parties = [[NSMutableArray alloc] init];
for (PillarModel *m in [DataManager sharedData].pillars)
{
[parties addObject:m.abbreviation];
}
NSMutableArray *yVals2 = [[NSMutableArray alloc] init];
NSMutableArray *benchMark = [[NSMutableArray alloc] init];
BOOL shouldAdd = YES;
for (PillarModel *pillar in [DataManager sharedData].pillars)
{ int totalPillarScore = (int)[[DataManager sharedData] getTotalScorePossible:[[DataManager sharedData] getIndividualQuestionResultsForPillar:pillar.pillarId]];
if ((isCompany ? [[DataManager sharedData] getCompanyScoresForCategory:pillar] : [[DataManager sharedData] getIndividualScoreForCategory:pillar.pillarId]) != 0)
{
shouldAdd = NO;
}
if (pillar.pillarId == numberOfPillars -1 && shouldAdd) {
[yVals2 addObject:[[ChartDataEntry alloc] initWithValue:0.01 xIndex:pillar.pillarId]];
}
else{
double val = (isCompany ? (double)[[DataManager sharedData] getCompanyScoresForCategory:pillar]/totalPillarScore : (double)[[DataManager sharedData] getIndividualScoreForCategory:pillar.pillarId] / totalPillarScore);
[yVals2 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:pillar.pillarId]];
}
float pillarBenchMark = [[[[[DataManager sharedData].benchmarkJSONDict objectForKey:hightlightedView.lowercaseString] objectForKey:pillar.title] objectForKey:#"pillar"] intValue];
float ifNoBenchMarkThanCompanyScore = (pillarBenchMark ? pillarBenchMark : (isCompany ? [[DataManager sharedData] getIndividualScoreForCategory:pillar.pillarId]: [[DataManager sharedData] getCompanyScoresForCategory:pillar]));
[benchMark addObject:[[ChartDataEntry alloc] initWithValue:ifNoBenchMarkThanCompanyScore/totalPillarScore xIndex:pillar.pillarId]];
}
NSMutableArray *xVals = [[NSMutableArray alloc] init];
for (int i = 0; i < numberOfPillars; i++)
{
[xVals addObject:parties[i % parties.count]];
}
NSString *label = (isCompany ? #"Company score" : #"Your score");
RadarChartDataSet *set2 = [[RadarChartDataSet alloc] initWithYVals:yVals2 label:label];
[set2 setColor:[DataManager sharedData].mainColor];
set2.drawFilledEnabled = YES;
set2.fillAlpha = 1.0f;
set2.lineWidth = 2.0;
RadarChartDataSet *benchMarkSet = [[RadarChartDataSet alloc] initWithYVals:benchMark label:#"Benchmark"];
[benchMarkSet setColor:[UIColor colorWithRed:125/255.0f green:125/255.0f blue:125/255.0f alpha:0.6f]];
benchMarkSet.drawFilledEnabled = YES;
benchMarkSet.fillAlpha = 0.0f;
benchMarkSet.lineWidth = 2.0;
benchMarkSet.highlightLineDashLengths = #[[NSNumber numberWithInt:2]];
benchMarkSet.highlightLineDashPhase = 2;
RadarChartData *data = [[RadarChartData alloc] initWithXVals:xVals dataSets:#[set2, benchMarkSet]];
[data setValueFont:[UIFont fontWithName:#"HelveticaNeue-Light" size:8.f]];
[data setDrawValues:NO];
self.radarChart.data = data;
}
The answer that was posted by 孙博弘 wasn't correct, but it led me to realise how easy this was to solve. This solves both the autoscaling problem and also now reaches the edge.
self.radarChart.yAxis.customAxisMin = 0;
self.radarChart.yAxis.customAxisMax = 1;
i'm afraid that you shouln't use customAxisMax directly as it use a Internal Access Levels.https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html
/// Flag indicating that the axis-max value has been customized
internal var _customAxisMax: Bool = false
Instead, you can use axisMaxValue to control it.
/// The maximum value for this axis.
/// If set, this value will not be calculated automatically depending on the provided data.
/// Use `resetCustomAxisMin()` to undo this.
public var axisMaxValue: Double
{
get
{
return _axisMaximum
}
set
{
_customAxisMax = true
_axisMaximum = newValue
}
}
//2016-06-12 update
public var factor: CGFloat
{
let content = _viewPortHandler.contentRect
return min(content.width / 2.0, content.height / 2.0)
/ CGFloat(_yAxis.axisRange)
}
RadarChartView will calculator a factor to transform values into pixels.
Maybe,you can use axisMinValue and axisMaxValue to control autoscale.

Executes same code more than once. How to use Goto statement inside for loop

I have code which is used more than two times in different condition in same function. So I decided to use Goto statement. But that code will be executed inside for loop. So I don't understand how to call same code in same function. I don't want to create one more function. My code is...
- (void)setSelectedSearchCriteria:(NSString *)storedValue storedTag:(NSString *)storedTag D_Key:(NSString *)D_Key D_Tag_Value:(NSString *)D_Tag_Value arrayMain:(NSMutableArray *)arrayMain bgView:(UIView *)bgView
{
//Add data
NSMutableArray *sArray = [[storedValue componentsSeparatedByString:#","] mutableCopy];
NSMutableArray *sTagArray = [[storedTag componentsSeparatedByString:#","] mutableCopy];
[sArray removeObject:#""];
[sTagArray removeObject:#""];
int maxTag = 0;
if (sTagArray.count != 0)
{
maxTag = [[sTagArray valueForKeyPath:#"#max.intValue"] intValue];
for (int i = maxTag + 1; i <= [D_Tag_Value intValue]; i++)
goto add_value;
}
else
goto add_value;
add_value:
{
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
}
storedValue = [[[sArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
storedTag = [[[sTagArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
[SEARCH_CRITERIAS setValue:storedValue forKey:D_Key];
[SEARCH_CRITERIAS_TAG setValue:storedTag forKey:D_Key];
}
Code inside add_value executed in for loop and also in else part. So I don't know how to manage this.
Define a block inside your function
void(^theBlock)(void) = ^(){
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
};
I don't fully understand what do you do in your add_value. If it can change to a block receive some parameters and return some value that would be better
after that you simply call the block
theBlock();
The code doesn't actually depend on the loop counter, so it isn't too hard to refactor the code so that you can simply execute the loop the appropriate number of times.
- (void)setSelectedSearchCriteria:(NSString *)storedValue storedTag:(NSString *)storedTag D_Key:(NSString *)D_Key D_Tag_Value:(NSString *)D_Tag_Value arrayMain:(NSMutableArray *)arrayMain bgView:(UIView *)bgView
{
//Add data
NSMutableArray *sArray = [[storedValue componentsSeparatedByString:#","] mutableCopy];
NSMutableArray *sTagArray = [[storedTag componentsSeparatedByString:#","] mutableCopy];
[sArray removeObject:#""];
[sTagArray removeObject:#""];
int loopCount = 1;
if (sTagArray.count != 0) {
int maxTag = [[sTagArray valueForKeyPath:#"#max.intValue"] intValue];
loopCount = [D_Tag_Value intValue] - maxTag;
}
for (int i = 0; i < loopCount ; i++) {
NSString *D_Value = [[arrayMain objectAtIndex:[D_Tag_Value intValue]] valueForKey:PARAMETER_KEY];
if (![sArray containsObject:D_Value])
{
[sArray addObject:D_Value];
[sTagArray addObject:D_Tag_Value];
}
//Add data
UIButton *btn = (UIButton *)[bgView viewWithTag:[D_Tag_Value intValue]];
[self setSelectedButtonStyle:btn];
}
storedValue = [[[sArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
storedTag = [[[sTagArray valueForKey:KEY_DESCRIPTION] componentsJoinedByString:#","] mutableCopy];
[SEARCH_CRITERIAS setValue:storedValue forKey:D_Key];
[SEARCH_CRITERIAS_TAG setValue:storedTag forKey:D_Key];
}

UITableView sorting

I've been brought in on this project where the previous developers made custom table cells and headers by using xib files and then registering the nibs like so:
[self.accountTable registerNib:[UINib nibWithNibName:kNonATITableViewCellLandscapeNib bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kNonATITableViewCellLandscapeIdentifier];
[self.accountTable registerNib:[UINib nibWithNibName:kNonATITableHeaderLandscapeNib bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kNonATITableHeaderLandscapeId];
The header files have buttons in them and uiimageviews. The buttons are for sorting, the uiimageviews for an arrow icon to show you the direction of the sort (asc, desc). All the buttons and imageviews are IBOutlets. All the buttons are linked to an IBAction:
- (IBAction)sortButtonTouched:(id)sender;
The file also has two other properties:
#property (nonatomic, assign) SortType currentSortingOption;
#property (nonatomic, strong) UIButton* btnLastTouched;
Here is sortButtonTouched:
- (IBAction)sortButtonTouched: (UIButton*) buttonTouched {
if (!self.btnLastTouched) {
self.btnLastTouched = buttonTouched;
}
NSString* strFieldToSort;
UIImageView* ivSortImage;
NSArray* arrSortIcons = [[NSArray alloc] initWithObjects:self.ivAccountSort,self.ivNameSort, self.ivAddressSort, self.ivCitySort, self.ivZipSort, self.ivLastCallSort, self.ivMileageSort, nil];
//get the image for the button selected
if (buttonTouched.tag == 0) {
strFieldToSort = #"customerNumber";
ivSortImage = self.ivAccountSort;
} else if (buttonTouched.tag == 1) {
strFieldToSort = #"customerName";
ivSortImage = self.ivNameSort;
} else if (buttonTouched.tag == 2) {
strFieldToSort = #"address";
ivSortImage = self.ivAddressSort;
} else if (buttonTouched.tag == 3) {
strFieldToSort = #"city";
ivSortImage = self.ivCitySort;
} else if (buttonTouched.tag == 4) {
strFieldToSort = #"zip";
ivSortImage = self.ivZipSort;
} else if (buttonTouched.tag == 5) {
strFieldToSort = #"lastCallDate";
ivSortImage = self.ivLastCallSort;
} else if (buttonTouched.tag == 6) {
strFieldToSort = #"mileage";
ivSortImage = self.ivMileageSort;
}
//set the sort option and add icon
if (!self.currentSortingOption) {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
} else {
if (![self.btnLastTouched isEqual:buttonTouched]) {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
} else {
if (self.currentSortingOption == SORT_ASC) {
self.currentSortingOption = SORT_DESC;
[ivSortImage setImage:[UIImage imageNamed:Descending_Icon]];
} else {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
}
}
}
//show and hide
for(int i=0; i<arrSortIcons.count; i++) {
UIImageView* ivThisImage = [arrSortIcons objectAtIndex:i];
if (buttonTouched.tag == i) {
[UIView animateWithDuration:.25 animations:^(void) {
ivThisImage.alpha = 1.0;
}];
} else {
[UIView animateWithDuration:.25 animations:^(void) {
ivThisImage.alpha = 0.0;
}];
}
}
//call back to routing view controller and sort results based on sort order and field selected
NSDictionary* dictUserData = [[NSDictionary alloc] initWithObjectsAndKeys:
#"Sort Non-ATI", #"Action",
strFieldToSort, #"Field To Sort",
[NSNumber numberWithLong:self.currentSortingOption], #"Sortng Option",
nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"rvc" object:self userInfo:dictUserData];
self.btnLastTouched = buttonTouched;
}
And the notification fires this method:
- (void) sortNonATIResults : (NSDictionary*) dictSortParams {
if (self.arrNonATIResults.count > 0) {
NSString* sortKey = [dictSortParams objectForKey:#"Field To Sort"];
//change the field to sort to match the customerInfo object properties...
NSNumber* numSortType = [dictSortParams objectForKey:#"Sortng Option"];
BOOL isAsc = YES;
if ([numSortType intValue] == 2) {
isAsc = NO;
}
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:isAsc];
NSArray* arrSortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray* arrSortedNonATIResults = (NSArray*)[self.arrNonATIResults sortedArrayUsingDescriptors:arrSortDescriptors];
self.arrNonATIResults = [arrSortedNonATIResults mutableCopy];
self.arrDatasource = self.arrNonATIResults;
[self.accountTable reloadData];
}
}
There are two problems right now. The icons are not showing up if the notification is sent. Comment out the notification and they function as expected. The other problem is that the property currentSortingOption doesn't retain it's value. I think both issues are related but I am not 100% sure. When the tableview is reloaded, does the header get instantiated again? This would make sense to me since then the uiimageviews would be reset with no image and the property would lose it's value and reset to 0 (it is the value of a typedef).
So, I am correct, how can I resolve this and if not, what could be causing the problems?
Thanks
OK, sorry for posting and then solving my problem right away, I guess sometimes you just need to write out the problem to find the solution. All I needed to do was not reload the table but just reload the rows. Here's the updated method:
(void) sortNonATIResults : (NSDictionary*) dictSortParams {
if (self.arrNonATIResults.count > 0) {
NSString* sortKey = [dictSortParams objectForKey:#"Field To Sort"];
//change the field to sort to match the customerInfo object properties...
NSNumber* numSortType = [dictSortParams objectForKey:#"Sortng Option"];
BOOL isAsc = YES;
if ([numSortType intValue] == 2) {
isAsc = NO;
}
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:isAsc];
NSArray* arrSortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray* arrSortedNonATIResults = (NSArray*)[self.arrNonATIResults sortedArrayUsingDescriptors:arrSortDescriptors];
self.arrNonATIResults = [arrSortedNonATIResults mutableCopy];
self.arrDatasource = self.arrNonATIResults;
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *indexPathArray = [[NSMutableArray alloc] init];
for (NSInteger section = 0; section < [self.accountTable numberOfSections]; ++section)
{
for (NSInteger row = 0; row < [self.accountTable numberOfRowsInSection:section]; ++row)
{
[indexPathArray addObject:[NSIndexPath indexPathForRow:row inSection:section]];
}
}
[self.accountTable reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone];
[self.accountTable scrollsToTop];
});
}
}

MapView Overlay Drawing

I have been facing problem which is mkoverlay color. When I open the mapview, sometimes instead of drawing walking path, it colors with biking activity. I do not know how to fix the problem. Even though I have not done any biking activity but it draws biking activity with blue color.
Here is the code implementation.
- (void)showLines {
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSArray* coordinate_array = [[NSArray alloc] init];
int arrayCount = 0;
// walking
NSData *data =[def objectForKey:#"walking_coordinate"];
NSMutableArray *walking_array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
coordinate_array = [NSArray arrayWithArray:walking_array];
arrayCount = (int)[walking_array count];
color = 1;
[self parseArray:coordinate_array withArrayCount:arrayCount];
// driving
data =[def objectForKey:#"driving_coordinate"];
NSMutableArray *driving_array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
coordinate_array = [NSArray arrayWithArray:driving_array];
arrayCount = (int)[driving_array count];
color = 2;
[self parseArray:coordinate_array withArrayCount:arrayCount];
// biking
data =[def objectForKey:#"biking_coordinate"];
NSMutableArray *biking_array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
coordinate_array = [NSArray arrayWithArray:biking_array];
arrayCount = (int)[biking_array count];
color = 3;
[self parseArray:coordinate_array withArrayCount:arrayCount];
}
- (void) parseArray:(NSArray *) coordinate_array withArrayCount:(int)arrayCount
{
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithCapacity:0];
for (int i = 0; i < arrayCount; i++) {
CoordinateModel *coord = [coordinate_array objectAtIndex:i];
[tempArray addObject:coord];
if ((int)coord.latitude == -1 || (int)coord.longitude == -1 || i == arrayCount-1) {
// this is end of one segment
[tempArray removeLastObject];
CLLocationCoordinate2D *pointsCoordinate = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * [tempArray count]);
for (int j = 0; j < [tempArray count]; j++) {
CoordinateModel *point = [tempArray objectAtIndex:j];
CLLocationCoordinate2D old_coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude);
pointsCoordinate[j] = old_coordinate;
// NSLog(#"(%f, %f)", old_coordinate.latitude, old_coordinate.longitude);
}
if ([tempArray count] > 0) {
int countTemp = (int)[tempArray count];
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:pointsCoordinate count:countTemp];
[mapView addOverlay:polyline];
[tempArray removeAllObjects];
}
}
}
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineView *lineView = [[MKPolylineView alloc] initWithPolyline:overlay];
lineView.lineWidth = 8;
if (color == 1) {
// walking
lineView.strokeColor = [UIColor greenColor];
lineView.fillColor = [UIColor greenColor];
}
else if(color == 2) {
// driving
lineView.strokeColor = [UIColor redColor];
lineView.fillColor = [UIColor redColor];
}
else if(color == 3) {
// biking
lineView.strokeColor = [UIColor blueColor];
lineView.fillColor = [UIColor blueColor];
}
else {
lineView.strokeColor = [UIColor blackColor];
lineView.fillColor = [UIColor blackColor];
}
return lineView;
}
return nil;
}
In the viewForOverlay delegate method, the overlay color is set using the external variable color which is set before calling parseArray for each type of overlay.
However, there is no guarantee when the delegate method will be called by the map view and it's possible for the delegate method to be called multiple times for the same overlay after you've already added all the overlays (eg. if you zoom/pan the map and the overlay comes back into view).
Since the last color value you set is 3 (for "biking"), any calls that the map view makes to the delegate method after the overlays are already added will end up drawing the overlay with the biking color.
To fix this, you need to be able to determine what color to draw the overlay inside the delegate method itself using some property of the overlay parameter (and not relying on some external variable).
The simplest way to do this is to use the MKPolyline's title property.
See this answer and this answer that explain the fact that MKPolyline has a title property.
So what you could do in your case is:
Make color a parameter that you pass to your parseArray method.
In the parseArray method, after creating polyline, set its title to the color:
polyline.title = [NSString stringWithFormat:#"%d", color];
In viewForOverlay, check the overlay's title property and set the color accordingly. See a specific example in different coloured polygon overlays (it shows it for polygon but the same can be done with polylines).
Here is the answer based on Anna answer to help whomever wants to see in code.
- (void)showLines2 {
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSArray* coordinate_array = [[NSArray alloc] init];
int arrayCount = 0;
// walking
NSData *data =[def objectForKey:#"walking_coordinate"];
NSMutableArray *walking_array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
coordinate_array = [NSArray arrayWithArray:walking_array];
arrayCount = (int)[walking_array count];
color = 1;
[self parseArray:coordinate_array withArrayCount:arrayCount withColor:color];
// driving
data =[def objectForKey:#"driving_coordinate"];
NSMutableArray *driving_array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
coordinate_array = [NSArray arrayWithArray:driving_array];
arrayCount = (int)[driving_array count];
color = 2;
[self parseArray:coordinate_array withArrayCount:arrayCount withColor:color];
// biking
data =[def objectForKey:#"biking_coordinate"];
NSMutableArray *biking_array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
coordinate_array = [NSArray arrayWithArray:biking_array];
arrayCount = (int)[biking_array count];
color = 3;
[self parseArray:coordinate_array withArrayCount:arrayCount withColor:color];
}
- (void) parseArray:(NSArray *) coordinate_array withArrayCount:(int)arrayCount withColor:(int)polyColor
{
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithCapacity:0];
for (int i = 0; i < arrayCount; i++) {
CoordinateModel *coord = [coordinate_array objectAtIndex:i];
[tempArray addObject:coord];
if ((int)coord.latitude == -1 || (int)coord.longitude == -1 || i == arrayCount-1) {
// this is end of one segment
[tempArray removeLastObject];
CLLocationCoordinate2D *pointsCoordinate = (CLLocationCoordinate2D *)malloc(sizeof(CLLocationCoordinate2D) * [tempArray count]);
for (int j = 0; j < [tempArray count]; j++) {
CoordinateModel *point = [tempArray objectAtIndex:j];
CLLocationCoordinate2D old_coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude);
pointsCoordinate[j] = old_coordinate;
// NSLog(#"(%f, %f)", old_coordinate.latitude, old_coordinate.longitude);
}
if ([tempArray count] > 0) {
int countTemp = (int)[tempArray count];
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:pointsCoordinate count:countTemp];
polyline.title = [NSString stringWithFormat:#"%d", color];
[mapView addOverlay:polyline];
[tempArray removeAllObjects];
}
}
}
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineView *lineView = [[MKPolylineView alloc] initWithPolyline:overlay];
lineView.lineWidth = 8;
// ActivityType currentActivityType = [DataManager sharedInstance].activityType;
if ([overlay.title isEqualToString:#"1"]) {
// walking
lineView.strokeColor = [UIColor greenColor];
lineView.fillColor = [UIColor greenColor];
}
else if([overlay.title isEqualToString:#"2"]) {
// driving
lineView.strokeColor = [UIColor redColor];
lineView.fillColor = [UIColor redColor];
}
else if([overlay.title isEqualToString:#"3"]) {
// biking
lineView.strokeColor = [UIColor blueColor];
lineView.fillColor = [UIColor blueColor];
}
else {
lineView.strokeColor = [UIColor blackColor];
lineView.fillColor = [UIColor blackColor];
}
return lineView;
}
return nil;
}

Resources