EKEventStore predicateForRemindersInCalendars; を介して NSPredicate を作成すると、それを EKEventStore fetchRemindersMatchingPredicate:completion:^ に渡します。完了コード ブロックによって提供されるリマインダー配列をループできますが、リマインダー配列への参照を保存しようとしたり、配列のコピーをローカル変数またはインスタンスに作成しようとしたりすると、変数、両方の配列は空のままです。リマインダー配列は決してそれらにコピーされません。
これは私が使用している方法です。この方法では、述語を作成し、それをイベント ストアに渡し、NSLog を介してタイトルを記録するすべてのリマインダーをループします。NSLog のおかげで実行時にリマインダーのタイトルを確認できますが、ローカルの arrayOfReminders オブジェクトは空です。また、各リマインダーを NSMutableArray のインスタンス変数に追加しようとしましたが、完了コード ブロックを終了すると、インスタンス変数は空のままです。
ここで何か不足していますか?アプリ全体で使用するすべてのリマインダーへの参照を取得できない理由を教えてください。EKEvents へのアクセスと保存にまったく問題はありませんが、何らかの理由で EKReminders では実行できません。
- (void)findAllReminders {
NSPredicate *predicate = [self.eventStore predicateForRemindersInCalendars:nil];
__block NSArray *arrayOfReminders = [[NSArray alloc] init];
[self.eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray *reminders) {
arrayOfReminders = [reminders copy]; //Does not work.
for (EKReminder *reminder in reminders) {
[self.remindersForTheDay addObject:reminder];
NSLog(@"%@", reminder.title);
}
}];
//Always = 0;
if ([self.remindersForTheDay count]) {
NSLog(@"Instance Variable has reminders!");
}
//Always = 0;
if ([arrayOfReminders count]) {
NSLog(@"Local Variable has reminders!");
}
}
eventStore ゲッターは、インスタンス化を実行し、イベント ストアへのアクセスを取得する場所です。
- (EKEventStore *)eventStore {
if (!_eventStore) {
_eventStore = [[EKEventStore alloc] init];
//respondsToSelector indicates iOS 6 support.
if ([_eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)]) {
//Request access to user calendar
[_eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
NSLog(@"iOS 6+ Access to EventStore calendar granted.");
} else {
NSLog(@"Access to EventStore calendar denied.");
}
}];
//Request access to user Reminders
[_eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
if (granted) {
NSLog(@"iOS 6+ Access to EventStore Reminders granted.");
} else {
NSLog(@"Access to EventStore Reminders denied.");
}
}];
} else { //iOS 5.x and lower support if Selector is not supported
NSLog(@"iOS 5.x < Access to EventStore calendar granted.");
}
for (EKCalendar *cal in self.calendars) {
NSLog(@"Calendar found: %@", cal.title);
}
[_eventStore reset];
}
return _eventStore;
}
最後に、遅延インスタンス化を使用して、remindersForTheDay インスタンス変数を初期化していることを示します。
- (NSMutableArray *)remindersForTheDay {
if (!_remindersForTheDay) _remindersForTheDay = [[NSMutableArray alloc] init];
return _remindersForTheDay;
}
Apple のドキュメントを読みましたが、これに答えることができる説明はありません。ブロック プログラミングのドキュメントを読んだところ、ブロック内から問題なくローカル変数とインスタンス変数にアクセスできると記載されていましたが、何らかの理由で上記のコードが機能しません。
Google で回答を探しましたが、まだこれを理解していません。
みんな、ありがとう!
ジョナサン。
更新:
それ以来、イベント ストアのインスタンス化、テーブル ビューのセットアップ、KVO のセットアップ、提供された推奨事項に従って UI の更新を行う新しいアプリを作成しました。以下のコードはアプリ全体です。UITableView は、リマインダー コンテンツで更新されることはありません。アプリが起動し、私を見つめます。reloadData メソッドを呼び出すと、最終的に UI がリマインダー データで更新されます。
私の NSLog は配列に 372 個のオブジェクトがあることを出力するため、リマインダー データは self.reminders に存在します。
最後に、デバイスで UITableView に触れると、「'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'」でアプリがクラッシュします。
@import EventKit;
#import "RTViewController.h"
@interface RTViewController ()
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) EKEventStore *eventStore;
@property (strong, nonatomic) NSArray *reminders;
@property (nonatomic) BOOL accessGranted;
@property (strong, nonatomic) NSDate *date;
@end
@implementation RTViewController
- (EKEventStore *)eventStore {
if (!_eventStore) {
_eventStore = [[EKEventStore alloc] init];
if ([_eventStore respondsToSelector:@selector(requestAccessToEntityType:completion:)]) {
[_eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
if (granted) self.accessGranted = YES;
else self.accessGranted = NO;
}];
}
[_eventStore reset];
}
return _eventStore;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.reminders = [[NSArray alloc] init];
self.date = [NSDate date]; //Returns NSDate allocated/init to NOW
NSPredicate *predicate = [self.eventStore predicateForRemindersInCalendars:nil]; //nil will cause all calendars to be used.
//Ran on different thread.
[self.eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray *reminders) {
_reminders = [reminders copy]; //NEVER happens.
[self updateReminders];
}];
//Setup the "radio" and watch ourself for when self.reminders is changed from the other thread
[self addObserver:self forKeyPath:@"reminders" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//If no data exists, then this will return 0 so no rows created.
return [self.reminders count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"cell"];
if (self.reminders) {
EKReminder *reminder = self.reminders[indexPath.row];
cell.detailTextLabel.text = reminder.title;
}
return cell;
}
//Called by self when self.remidners is changed. - NEVER gets called.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self.tableView reloadData];
}
- (void)updateReminders {
NSLog(@"%d objects found in array", [self.reminders count]);
[self.tableView reloadData];
}
@end