UITABLEView duplicate rows issue - ios

I cannot resolve this issue and I have no idea why my UITableView duplicates rows whenever I click on the segment control. I want to refresh the table view with new data when segment control changes. I have tried many things and googled it but could not find any solution. I would appreciated if someone can help me please. I am still learning xcode and a lot to learn. here is my code -
#import "citsViewController.h"
#import "citsParseOperation.h"
#import "citsFuelFinder.h"
#import "citsTableViewCell.h"
#import "citsAboutViewController.h"
#import "MBProgressHUD.h"
#import <CoreLocation/CoreLocation.h>
// this framework is imported so we can use the kCFURLErrorNotConnectedToInternet error code
#import <CFNetwork/CFNetwork.h>
#import <MapKit/MapKit.h>
#interface citsViewController ()
{
CLLocationManager *locationManager;
CLGeocoder *geocoder;
CLPlacemark *placemark;
NSString *currentLoc;
int productName;
}
#property (nonatomic) NSMutableArray *earthquakeList;
#property (nonatomic) citsFuelFinder *currentEarthquakeObject;
#property (nonatomic, weak) IBOutlet UILabel *locationLabel;
// queue that manages our NSOperation for parsing earthquake data
#property (nonatomic) NSOperationQueue *parseQueue;
#end
#pragma mark -
#implementation citsViewController
#synthesize nomatchesView;
#synthesize footerLabel;
#synthesize headerLabel;
#synthesize fuelType;
#synthesize bannerIsVisible;
- (void)viewDidLoad {
[super viewDidLoad];
//refresh the tableview
UIRefreshControl *refreshControl=[[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
self.refreshControl=refreshControl;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[infoButton addTarget:self action:#selector(aboutUs:) forControlEvents:UIControlEventTouchUpInside];
//add info button in the navigation controller
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton] ;
//initialize location manager
locationManager=[[CLLocationManager alloc] init];
//to get location
geocoder=[[CLGeocoder alloc] init];
//show network activity
[UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE;
locationManager.delegate=self;
locationManager.desiredAccuracy=kCLLocationAccuracyBest;
//call the location manager update function
[locationManager startUpdatingLocation];
}
-(void)refresh:(id)sender {
//update table data
[locationManager startUpdatingLocation];
[self.refreshControl endRefreshing];
[self.tableView reloadData];
}
- (void)dealloc {
// we are no longer interested in these notifications:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kAddEarthquakesNotificationName
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kEarthquakesErrorNotificationName
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSCurrentLocaleDidChangeNotification
object:nil];
}
/**
Handle errors in the download by showing an alert to the user. This is a very simple way of handling the error, partly because this application does not have any offline functionality for the user. Most real applications should handle the error in a less obtrusive way and provide offline functionality to the user.
*/
- (void)handleError:(NSError *)error {
NSString *errorMessage = [error localizedDescription];
NSString *alertTitle = NSLocalizedString(#"Error", #"Title for alert displayed when download or parse error occurs.");
NSString *okTitle = NSLocalizedString(#"OK ", #"OK Title for alert displayed when download or parse error occurs.");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle message:errorMessage delegate:nil cancelButtonTitle:okTitle otherButtonTitles:nil];
[alertView show];
}
/**
Our NSNotification callback from the running NSOperation to add the earthquakes
*/
- (void)addEarthquakes:(NSNotification *)notif {
assert([NSThread isMainThread]);
[self addEarthquakesToList:[[notif userInfo] valueForKey:kEarthquakeResultsKey]];
}
/**
Our NSNotification callback from the running NSOperation when a parsing error has occurred
*/
- (void)earthquakesError:(NSNotification *)notif {
assert([NSThread isMainThread]);
[self handleError:[[notif userInfo] valueForKey:kEarthquakesMessageErrorKey]];
}
/**
The NSOperation "ParseOperation" calls addEarthquakes: via NSNotification, on the main thread which in turn calls this method, with batches of parsed objects. The batch size is set via the kSizeOfFuelPumpBatch constant.
*/
- (void)addEarthquakesToList:(NSArray *)earthquakes {
NSInteger startingRow = [self.earthquakeList count];
NSInteger earthquakeCount = [earthquakes count];
NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:earthquakeCount];
for (NSInteger row = startingRow; row < (startingRow+earthquakeCount); row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
[indexPaths addObject:indexPath];
}
NSLog(#"record count %d",earthquakeCount);
[self.earthquakeList addObjectsFromArray:earthquakes];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
//[self.tableView reloadData];
}
#pragma mark - UITableViewDelegate
// The number of rows is equal to the number of earthquakes in the array.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.earthquakeList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kEarthquakeCellID = #"EarthquakeCellID";
citsTableViewCell *cell = (citsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:kEarthquakeCellID];
if(cell==nil)
{
cell=[[citsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kEarthquakeCellID];
}
// Get the specific earthquake for this row.
citsFuelFinder *earthquake = (self.earthquakeList)[indexPath.row];
[cell configureWithEarthquake:earthquake];
return cell;
}
/**
* When the user taps a row in the table, display the USGS web page that displays details of the earthquake they selected.
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *buttonTitle = NSLocalizedString(#"Cancel", #"Cancel");
//NSString *buttonTitle1 = NSLocalizedString(#"Show USGS Site in Safari", #"Show USGS Site in Safari");
NSString *buttonTitle2 = NSLocalizedString(#"Show Location in Maps", #"Show Location in Maps");
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:buttonTitle destructiveButtonTitle:nil
otherButtonTitles: buttonTitle2, nil];
[sheet showInView:self.view];
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 500)];
headerLabel=[[UILabel alloc] initWithFrame:CGRectMake(10, 1.0, 300, 25)];
headerLabel.numberOfLines=0;
fuelType=[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"ULP", #"PULP",#"Diesel",#"LPG", nil]];
fuelType.frame = CGRectMake(10, 30, 300,25);
//set first segment selected
[fuelType setSelectedSegmentIndex:0];
[fuelType addTarget:self action:#selector(segmentedControlHasChangedValue) forControlEvents:UIControlEventValueChanged];
headerLabel.font=[UIFont systemFontOfSize:10.0];
[headerView insertSubview:fuelType aboveSubview:headerLabel];
[headerView addSubview:headerLabel];
[headerView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"footer.gif"]]];
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 60;
}
#pragma mark -
/**
* Called when the user selects an option in the sheet. The sheet will automatically be dismissed.
*/
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex {
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
citsFuelFinder *earthquake = (citsFuelFinder *)(self.earthquakeList)[selectedIndexPath.row];
switch (buttonIndex) {
case 0: {
// open the earthquake info in Maps
// create a map region pointing to the earthquake location
CLLocationCoordinate2D location = (CLLocationCoordinate2D) { earthquake.latitude, earthquake.longitude };
NSValue *locationValue = [NSValue valueWithMKCoordinate:location];
MKCoordinateSpan span = (MKCoordinateSpan) { 2.0, 2.0 };
NSValue *spanValue = [NSValue valueWithMKCoordinateSpan:span];
NSDictionary *launchOptions = #{ MKLaunchOptionsMapTypeKey : #(MKMapTypeStandard),
MKLaunchOptionsMapCenterKey : locationValue,
MKLaunchOptionsMapSpanKey : spanValue,
MKLaunchOptionsShowsTrafficKey : #(NO),
MKLaunchOptionsDirectionsModeDriving : #(NO) };
// make sure the map item has a pin placed on it with the title as the earthquake location
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:location addressDictionary:nil];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:earthquake.location];
[mapItem openInMapsWithLaunchOptions:launchOptions];
break;
}
}
[self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
// Regiser for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
HUD.labelText = #"Loading";
HUD.detailsLabelText = #"updating data";
HUD.square = YES;
self.earthquakeList = [NSMutableArray array];
self.currentEarthquakeObject=nil;
if([self.earthquakeList count] >0)
{
[self.earthquakeList removeAllObjects];
[self.tableView reloadData];
}
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addEarthquakesToList:) onTarget:self withObject:nil animated:YES];
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
NSString *latitude=[NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
NSString *longitude=[NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
citsTableViewCell *tcell = [[citsTableViewCell alloc] init];
//set the latitude and longitude
tcell.lon =[longitude doubleValue];
tcell.lat = [latitude doubleValue];
NSLog(#"Lat:%#, Lon:%#", latitude,latitude);
}
//stop updating location
[locationManager stopUpdatingLocation];
//reverse geocoding
NSLog(#"Resolving the address");
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
currentLoc=[NSString stringWithFormat:#"%#",placemark.locality];
if(currentLoc == NULL)
{
currentLoc=#"N/A";
}
NSLog(#"%#",currentLoc);
//add text to headertext
NSDate *currDate=[NSDate date];
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yy HH:mm:ss"];
NSString *dateString=[dateFormatter stringFromDate:currDate];
NSString *loc=[NSString stringWithFormat:#"Your location: %#, Updated on: %#", currentLoc, dateString ];
[dateFormatter setDateFormat:#"EEEE, dd/MM/yyyy"];
NSString *headerDate=[dateFormatter stringFromDate:currDate];
headerLabel.text=[NSString stringWithFormat:#"Prices for: %#\n%#", headerDate,loc];
//currentLoc=[NSString stringWithFormat:#"%#", placemark.locality];
currentLoc=#"Mirrabooka";
if(productName==0)
{
productName=1;
}
NSString *prdStr=[[NSString alloc] initWithFormat:#"%d", productName];
NSString *str =[currentLoc stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSString *feedURLString =[[NSString alloc] initWithFormat: #"http://www.fuelwatch.wa.gov.au/fuelwatch/fuelWatchRSS?Product=%#&Suburb=%#", prdStr,str ];
NSURLRequest *earthquakeURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURLString]];
NSLog(#"%#",feedURLString);
// send the async request (note that the completion block will be called on the main thread)
//
// note: using the block-based "sendAsynchronousRequest" is preferred, and useful for
// small data transfers that are likely to succeed. If you doing large data transfers,
// consider using the NSURLConnectionDelegate-based APIs.
//
[NSURLConnection sendAsynchronousRequest:earthquakeURLRequest queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// back on the main thread, check for errors, if no errors start the parsing
//
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// here we check for any returned NSError from the server, "and" we also check for any http response errors
if (error != nil) {
[self handleError:error];
}
else {
// check for any response errors
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ((([httpResponse statusCode]/100) == 2) ) {
// Update the UI and start parsing the data,
// Spawn an NSOperation to parse the earthquake data so that the UI is not
// blocked while the application parses the XML data.
//
citsParseOperation *parseOperation = [[citsParseOperation alloc] initWithData:data];
[self.parseQueue addOperation:parseOperation];
}
else {
NSString *errorString =
NSLocalizedString(#"HTTP Error", #"Error message displayed when receving a connection error.");
NSDictionary *userInfo = #{NSLocalizedDescriptionKey : errorString};
NSError *reportError = [NSError errorWithDomain:#"HTTP"
code:[httpResponse statusCode]
userInfo:userInfo];
[self handleError:reportError];
}
}
}];
// Start the status bar network activity indicator.
// We'll turn it off when the connection finishes or experiences an error.
//
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
self.parseQueue = [NSOperationQueue new];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(addEarthquakes:)
name:kAddEarthquakesNotificationName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(earthquakesError:)
name:kEarthquakesErrorNotificationName object:nil];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
NSLog(#"%d",[self.earthquakeList count]);
}
-(void)clearData{
[self.tableView beginUpdates];
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (int i = [self.tableView numberOfRowsInSection:0] - 1; i >= 0; i--)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:0]];
NSLog(#"Deleted: %d",i);
}
[self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationFade];
[self.earthquakeList removeAllObjects];
[self.tableView endUpdates];
//[self.tableView reloadData] ;
}
-(void)segmentedControlHasChangedValue{
int product;
product=fuelType.selectedSegmentIndex;
switch (product) {
case 0:
productName=1;
[locationManager startUpdatingLocation];
[self clearData];
break;
case 1:
productName=2;
[self clearData];
[locationManager startUpdatingLocation];
break;
case 2:
productName=4;
[self clearData];
[locationManager startUpdatingLocation];
break;
case 3:
prod![enter image description here][1]uctName=5;
[self clearData];
[locationManager startUpdatingLocation];
break;
}
NSLog(#"%d",productName);
return;
}
#end

Try removing all previous object from the self.earthquakeList before adding new 1. use [self.earthquakeList removeAllObjects]

Remove this Line,
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
And uncomment,
[self.tableView reloadData];
You are Already updating the data set, So you just need to reload the tableView instead of explicitly insert row.

Related

How to open Edit Contacts Screen for Specific Contact

I am working on a iOS Application in which I have to add contacts in Address Book.
I want to open Edit Contact Screen Whenever user Tries to add duplicate contact.
But I don't know how to do that.Currently I am only able to show a message only.
I am getting all contacts list as:
NSArray *allContacts = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBookRef);
Then I am itterating through it and check for existing one.If it exists then I am showing a message else I will add it to the addressbook.
for (id record in allContacts){
ABRecordRef thisContact = (__bridge ABRecordRef)record;
if (CFStringCompare(ABRecordCopyCompositeName(thisContact),
ABRecordCopyCompositeName(pet), 0) == kCFCompareEqualTo){
//The contact already exists!
NSLog(#"contact exosts");
}
else
{
ABAddressBookAddRecord(addressBookRef, pet, nil);
ABAddressBookSave(addressBookRef, nil);
ABMultiValueAddValueAndLabel(phoneNumbers, (__bridge CFStringRef)petPhoneNumber, kABPersonPhoneMainLabel, NULL);
NSLog(#"contacts Added");
}
}
How can I open following screen when user Tries to add duplicate contact:
I searched SO and find following questions but this doesn't help me.
Question 1
Question 2
And Is it possible to do so or not.Please any one assist me to achieve this feature if it is feasible.
See here .h
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
#interface ContactsViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, ABPersonViewControllerDelegate>
{
IBOutlet UITableView *tblContacts;
NSMutableArray *arrContacts;
}
#end
And .m
#import "ContactsViewController.h"
#interface ContactsViewController ()
{
UIAlertController *action;
}
#end
#implementation ContactsViewController
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Contacts";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNewContact:)];
[self getContactsUsingAddressbook];
}
#pragma mark - Methods
// ------- Deprecated (in iOS 9.0) ----------
- (void)getContactsUsingAddressbook
{
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if (status == kABAuthorizationStatusDenied || status == kABAuthorizationStatusRestricted)
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:#"This app previously was refused permissions to contacts; Please go to settings and grant permission to this app so it can use contacts" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:TRUE completion:nil];
return;
}
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (!addressBook)
{
NSLog(#"ABAddressBookCreateWithOptions error: %#", CFBridgingRelease(error));
return;
}
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (error)
{
NSLog(#"ABAddressBookRequestAccessWithCompletion error: %#", CFBridgingRelease(error));
}
if (granted)
{
NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
arrContacts = [NSMutableArray arrayWithArray:allPeople];
[tblContacts reloadData];
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:nil message:#"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
});
}
CFRelease(addressBook);
});
}
#pragma mark - Tableview delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return arrContacts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
//if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
// ------- Deprecated (in iOS 9.0) ----------
ABRecordRef person = (__bridge ABRecordRef)arrContacts[indexPath.row];
NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", firstName, lastName];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// ------- Deprecated (in iOS 9.0) ----------
ABPersonViewController *personController = [[ABPersonViewController alloc] init];
personController.personViewDelegate = self;
personController.displayedPerson = (__bridge ABRecordRef)arrContacts[indexPath.row];
personController.allowsEditing = YES;
personController.allowsActions = YES;
[self.navigationController pushViewController:personController animated:TRUE];
}
#pragma mark - ABPersonview delegate
- (BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
return TRUE;
}
And see in my simulator
You can edit contact as following
Here you have to add
// ------- Deprecated (in iOS 9.0)
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
ABPersonViewController *personController = [[ABPersonViewController alloc] init];
personController.personViewDelegate = self;
personController.displayedPerson = (__bridge ABRecordRef)arrContacts[indexPath.row];
personController.allowsEditing = YES;
personController.allowsActions = YES;
[self.navigationController pushViewController:personController animated:TRUE];
And here
#import <Contacts/Contacts.h>
#import <ContactsUI/ContactsUI.h>
// -------- This is not working for me, I got error
CNContact *contact = [arrContacts objectAtIndex:indexPath.row];
NSArray *keys = #[CNContactIdentifierKey, CNContactEmailAddressesKey, CNContactBirthdayKey, CNContactImageDataKey, CNContactPhoneNumbersKey, [CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]];
CNContactViewController *contactController = [CNContactViewController viewControllerForContact:contact];
contactController.delegate = self;
contactController.allowsEditing = YES;
contactController.allowsActions = YES;
contactController.displayedPropertyKeys = keys;
[self.navigationController pushViewController:contactController animated:TRUE];
See here Contact is missing some of the required key descriptors in ios
But still I have not found solution , If you have please tell me
Here is the answer
when bind arrayOfContact That time have to Provide key with [CNContactViewController descriptorForRequiredKeys].
NSArray *keys = #[CNContactGivenNameKey,CNContactFamilyNameKey,CNContactOrganizationNameKey, CNContactPhoneNumbersKey, CNContactEmailAddressesKey,CNContactPostalAddressesKey,[CNContactViewController descriptorForRequiredKeys]]
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
when open existing Contact
CNContactViewController *contactController = [CNContactViewController viewControllerForContact:contact];
contactController.delegate = self;
contactController.allowsEditing = YES;
contactController.allowsActions = YES;
[self.navigationController pushViewController:contactController animated:TRUE];

HMAccessoryDelegates not calling on Button action

I am working on Homekit iOS app. I have a question that I have an accessory and When I change its power characteristic value using the HomeKit Simulator the delegates of HMAccessory are caliing but in case If I change the powr characteristic value programmatically (Using the writevalue ) the delegate methods are not being called. Please let me know any ideas of suggestions.
Code
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
selectedDevice.delegate = self;
}
HMAccessoryDelegate
- (void)accessory:(HMAccessory *)accessory service:(HMService *)service didUpdateValueForCharacteristic:(HMCharacteristic *)characteristic;
{
NSLog(#"changed");
}
Write Function
UISwitch *sw = [[UISwitch alloc] initWithFrame:CGRectMake(230, 5, 51, 31)];
[cell addSubview:sw];
sw.on = YES;
[sw addTarget:self action:#selector(updateState:) forControlEvents:UIControlEventValueChanged];
-(void)updateState:(UISwitch*)sender
{
HMCharacteristic *characteristic = self.selectedService.characteristics[tag];
[characteristic enableNotification:YES completionHandler:^(NSError *error)
{
if(!error)
{
}
}];
if([characteristic.characteristicType isEqualToString:HMCharacteristicTypePowerState])
{
id val = characteristic.value;
NSString *str = [NSString stringWithFormat:#"%#",val];
if([str isEqualToString:#"0"])
{
id a = characteristic.value;
BOOL b = [a boolValue];
NSNumber *c = [NSNumber numberWithBool:!b];
AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
[characteristic writeValue:c completionHandler:^(NSError *error) {
if (error) {
UIAlertView *alertController = [[UIAlertView alloc] initWithTitle:#"Error" message:[appDel handleErrorCodes:error.code] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertController show];
return;
}
else
{
[serviceCharacteristicsTableView reloadData];
}
}];
}
}
Please let me know if I am not clear
The documentation says that the delegate method is not called when you set the value programatically:
This method is called as a result of a change in value initiated by
the accessory. Programmatic changes initiated by the app do not result
in this method being called.
If you want to do something after writing the characteristic's value succeeded (or failed), you can do it in the completionHandler: block of writeValue:completionHandler: method.

didEnterRegion and startRangingForBeacons not being called

I've been having trouble figuring out why startRangingBeaconsInRegion is never called. I know for certain the startMonitoringForRegion is called, and I tried outputting the mRegionsArray as a string and it worked. But the didEnterRegion is not being called however. And I tried walking around back and forth trying to get a signal from my beacons (ie, entering the region), but no luck. I can't wrap my head around what might be wrong, went through a lot of questions on here and none of them mirrored my issue.
I have a Beacons table view and each cell is supposed to contain information (major, minor) on each beacon. Except, these cells aren't being filled because the ranging is not happening. :( I even tried to change it so it only detects one beacon. I know the problem doesn't lie within the Beacon class I created because the loadTestData() function works...
If anyone can help, it would be much appreciated.
BeaconTableViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface BeaconTableViewController : UITableViewController <CLLocationManagerDelegate>
#property (strong, nonatomic) CLBeaconRegion *beaconRegion;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
BeaconTableViewController.m
#import "BeaconTableViewController.h"
#import "Beacon.h"
#import "BeaconTableViewCell.h"
#interface BeaconTableViewController () <UITableViewDataSource, UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *beaconsTableView;
#property (strong, nonatomic) NSMutableArray *beacons;
#end
#implementation BeaconTableViewController
- (void)loadTestData {
self.beacons = [[NSMutableArray alloc] init];
Beacon *beacon1 = [[Beacon alloc] init];
beacon1.major = [[NSNumber alloc] initWithInt:21311];
beacon1.minor = [[NSNumber alloc] initWithInt:21331];
[self.beacons addObject:beacon1];
Beacon *beacon2 = [[Beacon alloc] init];
beacon2.major = [[NSNumber alloc] initWithInt:10011];
beacon2.minor = [[NSNumber alloc] initWithInt:10012];
[self.beacons addObject:beacon2];
Beacon *beacon3 = [[Beacon alloc] init];
beacon3.major = [[NSNumber alloc] initWithInt:65535];
beacon3.minor = [[NSNumber alloc] initWithInt:30136];
[self.beacons addObject:beacon3];
[self.beaconsTableView beginUpdates];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:self.beacons.count-1 inSection:0];
[self.beaconsTableView insertRowsAtIndexPaths:#[newIndexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.beaconsTableView endUpdates];
}
- (void)initRegion {
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:#"E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:#"AB Region"];
[self.locationManager startMonitoringForRegion:self.beaconRegion];
}
- (void) locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region {
UIAlertView *alertMonitoring = [[UIAlertView alloc] initWithTitle:#"User Notification"
message:#"Started monitoring for region."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertMonitoring show];
NSSet *mRegions = [self.locationManager monitoredRegions];
NSArray *mRegionsArray = [mRegions allObjects];
NSString *str = [mRegionsArray componentsJoinedByString:#","];
UIAlertView *alertRegion = [[UIAlertView alloc] initWithTitle:#"User Notification"
message:str
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertRegion show];
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
UIAlertView *alertRanging = [[UIAlertView alloc] initWithTitle:#"User Notification"
message:#"Started ranging."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertRanging show];
CLBeacon *foundBeacon = [beacons firstObject];
Beacon *beacon;
beacon.major = foundBeacon.major;
beacon.minor = foundBeacon.minor;
UIAlertView *alertBeaconFound = [[UIAlertView alloc] initWithTitle:#"User Notification"
message:[[[#"Major: " stringByAppendingString:[NSString stringWithFormat:#"%#", beacon.major]] stringByAppendingString:#", Minor: "] stringByAppendingString:[NSString stringWithFormat:#"%#", beacon.minor]]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertBeaconFound show];
[self.beacons addObject:beacon];
[self.beaconsTableView beginUpdates];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:self.beacons.count-1 inSection:1];
[self.beaconsTableView insertRowsAtIndexPaths:#[newIndexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.beaconsTableView endUpdates];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"User Notification"
message:#"Did enter region."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
[self.locationManager stopRangingBeaconsInRegion:self.beaconRegion];
}
- (void)viewDidLoad {
[super viewDidLoad];
// [self loadTestData];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
[self initRegion];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.beacons count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BeaconTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"BeaconCell" forIndexPath:indexPath];
Beacon *beacon = [self.beacons objectAtIndex:indexPath.row];
cell.beacon = beacon;
return cell;
}
Under iOS 8, Apple added some new requirements to using the location manager (And iBeacons are a Location Manager function)
You have to add the keys NSLocationAlwaysUsageDescription and/or NSLocationWhenInUseUsageDescription to your info.plist file, and then before trying to start monitoring beacons you have to check the authorization status and if it is kCLAuthorizationStatusNotDetermined, you have to make a new call, either requestAlwaysAuthorization or requestWhenInUseAuthorization
The code might look something like this:
CLAuthorizationStatus status =[CLLocationManager authorizationStatus];
if (status ==kCLAuthorizationStatusDenied)
{
NSLog(#"Location manager denied");
}
theLocManager = [[CLLocationManager alloc] init];
theLocManager.delegate = self;
if (status == kCLAuthorizationStatusNotDetermined
&& [theLocManager respondsToSelector: #selector(requestAlwaysAuthorization)])
[theLocManager requestAlwaysAuthorization];
(You have to add the check to make sure the location manage responds to the requestAlwaysAuthorization or requestWhenInUseAuthorization method, since they are only available in iOS >= 8.)
The thing I don't like about this OS change is that if you don't make the request call, your calls to start monitoring beacons fail silently.

I can not hide the view before the function finish

I would like to hide the multipleSearchView before the start of the closestStation function but without success, it hides but after closestStation finish
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath
{
if (tableView == self.localityTableView){
[self.multipleSearchView setHidden:TRUE];
[self closestStation:locality.latitude :locality.longitude];
}
}
- (void) closestStation :(float) latitude :(float) longitude{
// this function takes 3 or 4 second
}
That's because you have a heavy-lifting operation in main queue. Put your -closestStation: to background queue to solve this.
Try with performSelectorInBackground.
UI updates also takes a while (in next cycle). Try with that:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
if (tableView == self.localityTableView){
[self.multipleSearchView setHidden:TRUE];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self closestStation:locality.latitude :locality.longitude];
});
}
}
- (void) closestStation :(float) latitude :(float) longitude{
// SQLite query here to find the closest places
if (self.places.count == 0){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No Results"
message:#"No results match your
search. try with other criteria"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
else {
[self displayAnnotations];
}
}
-(void) displayAnnotations{
NSMutableArray *annotations = [[NSMutableArray alloc] init];
for (Station *item in self.places)
{
PlaceAnnotation *annotation = [[PlaceAnnotation alloc] init];
annotation.coordinate = item.coordinate;
annotation.title = item.stationName;
annotation.subtitle = item.address1;
annotation.station = item;
[annotations addObject:annotation];
}
if (annotations.count > 0){
[self.mapView addAnnotations:annotations];
}
}

iOS: Adding data to table view from a custom method

I have an iPhone app connects to a server using OAuth. On success, it fetches the a user from the server. Again, upon success, it adds an item to the array of objects that populates the table view. Here is the code that does this:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
if (editing) {
[super setEditing:YES animated:YES];
self.backButton = self.navigationItem.leftBarButtonItem;
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(signInWithCatapult)];
self.navigationItem.leftBarButtonItem = leftButton;
} else {
[super setEditing:NO animated:YES];
self.navigationItem.leftBarButtonItem = self.backButton;
}
}
- (void)signInWithCatapult
{
[self signOut];
GTMOAuth2Authentication *auth = [self catapultAuthenticaiton];
NSURL *authURL = [NSURL URLWithString:#"https://oauth.lvh.me:3000/oauth/authorize"];
GTMOAuth2ViewControllerTouch *viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:auth
authorizationURL:authURL
keychainItemName:kCatapultKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self navigationController] pushViewController:viewController animated:YES];
}
- (GTMOAuth2Authentication *)catapultAuthenticaiton
{
NSURL *tokenURL = [NSURL URLWithString:kDoorkeeperTokenURL];
NSString *redirectURI = #"https://catapultcentral.com/iOSClientCallback";
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2Authentication authenticationWithServiceProvider:#"Catapult Central"
tokenURL:tokenURL
redirectURI:redirectURI
clientID:kDoorkeeperClientID
clientSecret:kDoorkeeperClientSecret];
return auth;
}
- (void)signOut
{
}
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error
{
if (error != nil) {
#if DEBUG
NSLog(#"ERROR: %#", error);
#endif
} else {
NSURL *url = [NSURL URLWithString:#"https://api.lvh.me:3000/api/users/me"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request];
[fetcher setAuthorizer:auth];
[fetcher beginFetchWithDelegate:self didFinishSelector:#selector(currentUserFetcher:finishedWithData:error:)];
}
}
- (void)currentUserFetcher:(GTMHTTPFetcher *)fetcher
finishedWithData:(NSData *)data
error:(NSError *)error
{
if (error != nil) {
#if DEBUG
NSLog(#"ERROR: %#", error);
#endif
} else {
NSLog(#"Before: %#", self.accounts);
[self.tableView beginUpdates];
[self.accounts addObject:#"Success!!!"];
[self.tableView endUpdates];
// [self.tableView reloadData];
NSLog(#"After %#", self.accounts);
}
}
It's in the currentUserFetcher:finishedWithData:error: method that I add the object to the self.accounts mutable array. Now if I use this code it doesn't work:
[self.tableView beginUpdates];
[self.accounts addObject:#"Success!!!"];
[self.tableView endUpdates];
It fails at the line [self.tableView endUpdates]; with the following error message:
2013-03-28 08:56:21.040 Catapult for iOS[55012:c07] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1054
And on the endUpdates line, XCode is complaining saying Thread 1: breakpoint 1.3. Now, if I use this code, it works normally:
[self.accounts addObject:#"Success!!!"];
[self.tableView reloadData];
Now I suspect that it is failing because I add an object to the self.accounts instance variable but I don't actually add the cell. So my question is: How do I add a cell to the tableView from the currentUserFetcher:finishedWithData:error: method?
If you just override this method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
Calling [UITableView reloadData] should just work itself out. The UITableViewController will just ask the amount of data (cells) that are there (using "tableView:numberOfRowsInSection:") and is requesting the Cell for every indexPath using the first mentioned method.

Resources