PDFを作る/印刷する

全体の流れ
1.PDFデータを格納する変数またはファイルを用意する
2.ページのスタート
3.内容を書く
4.プリンタに送る

※今回はPDFをデータにしたけど、ファイルにして、それをUIDocumentInteractionControllerに送っても良い。

//PDFデータを格納する変数またはファイルを用意する(今回はデータ)
NSMutableData*pdfData=[[NSMutableData alloc]initWithCapacity:0];
UIGraphicsBeginPDFContextToData(pdfData, CGRectZero, nil);

//1ページ目スタート(A4サイズ)
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0,0,612,792),nil);

//文字列書き込み
[@”書き込む文字列” drawInRect:CGRectMake(0,0,100,30)];
//イメージ書き込み
[[UIImage imageNamed:@”書き込むイメージファイル”] drawInRect:CGRectMake(0,0,100,100)];

//書き込み終了
UIGraphicsEndPDFContext();

//プリンタに送る
UIPrintInfo*printInfo=[UIPrintInfo printInfo];
printInfo.jobName=@”printjob”;
printInfo.orientation=UIPrintInfoOrientationPortrait;
printInfo.duplex=UIPrintInfoDuplexLongEdge;
printInfo.outputType=UIPrintInfoOutputGeneral;
UIPrintInteractionController*pCon=[UIPrintInteractionController sharedPrintController];
pCon.printInfo=printInfo;
pCon.printingItem=pdfData;
pCon.showsPageRange=NO;
pCon.delegate=self;

void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) = ^(UIPrintInteractionController *printDocController, BOOL completed, NSError *error) {
        NSLog(@”%d”,completed);
        if (!completed && error) {
            NSLog(@”PrintError %@”, error);
        }
};
[pCon presentAnimated:YES completionHandler:completionHandler];

//UIPrintInteractionControllerのdelegate
– (UIViewController *)printInteractionControllerParentViewController:(UIPrintInteractionController *)printInteractionController{
    //UIPrintInteractionControllerの親になる(下になる)ViewControllerを返す
    return self;
}

CloudKitデータベースから通知を発行する/受け取る

全体の流れ
1.CloudKitDatabaseから通知がいくようにセットする
2.リモート通知が受け取れるようにセットする

//CloudKitデータベースから通知がいくようにセットする。どこで実行してもOK。
NSPredicate*predicate=[NSPredicate predicateWithFormat:@”TRUEPREDICATE”];
CKSubscription*subscription=[[CKSubscription alloc]initWithRecordType:@”neko” predicate:predicate options:CKSubscriptionOptionsFiresOnRecordCreation|CKSubscriptionOptionsFiresOnRecordDeletion];
subscription.notificationInfo=[[CKNotificationInfo alloc]init];
subscription.notificationInfo.alertBody=@”データベースからの通知だよ”;
subscription.notificationInfo.shouldBadge=YES;
subscription.notificationInfo.shouldSendContentAvailable=YES;
[savedSubscriptions addObject:subscription];

//リモートの通知が受け取れるようにセットする

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UIUserNotificationSettings*settings=[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil];
    [application registerForRemoteNotifications];
    [application registerUserNotificationSettings:settings];
    [application registerForRemoteNotifications];
    …
    return YES;
}

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
    CKNotification*notif=[CKNotification notificationFromRemoteNotificationDictionary:userInfo];
    if(notif.notificationType==CKNotificationTypeQuery){
        //ここでキャッチした時の処理を書く
    }
    completionHandler(UIBackgroundFetchResultNoData);
}

訂正:
notif.notificationType==CKNotificationTypeQueryって書くと、アクティブ・バックグラウンドの時は実行されるが、アプリ未起動時は実行されない。返ってきてる値が違うのかもしれない。要確認。
とりあえずif文を外して書いた。

CKRecordをNSUserDefaultsやファイルでキャッシュする場合

全体の流れ
1.データを格納しておくカスタムクラスを作る(NSObject<NSCoding>)
2.CKAssetはそのまま保存できないのでNSDataで保存できるようにする
2.NSCodingに必要な物をメソッドを実装する
4. 必要なデータを格納しファイルに保存したりする

//データを格納しておくカスタムクラスを作る
【DataClass.h】
#import <Foundation/Foundation.h>
@import CloudKit;
@interface DataClass : NSObject<NSCoding>{
    CKRecord*record;
    NSData*photoData;
}
@property(readwrite)CKRecord*record;
@property(readwrite)NSData*photoData;
@end

【DataClass.m】
#import “DataClass.h”
@implementation DataClass
@synthesize record;
@synthesize photoData;
-(id)initWithCoder:(NSCoder *)aDecoder{
    self.record=[aDecoder decodeObjectForKey:@”record”];
    self.photoData=[aDecoder decodeObjectForKey:@”photoData”];
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:self.record forKey:@”record”];
    [aCoder encodeObject:self.photoData forKey:@”photoData”];
}
@end

//データを格納するクラスを準備する
DataClass*dataClass=[[DataClass alloc]init];
NSMutableArray*dataArray=[[NSMutableArray alloc]initWithCapacity:0];

//データを格納する
dataClass.record=record;
CKAsset*asset=[record valueForKey:@”photoAsset”];
dataClass.photoData=[[NSData alloc]  initWithContentsOfFile:asset.fileURL.path];
[dataArray addObject:dataClass];

//今回は日付のファイル名で保存するのでその準備
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setDateFormat:@”yyyyMMdd”];

//ファイルの作成準備
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString*fileName=[[paths objectAtIndex:0] stringByAppendingFormat:@”/cacheData/%@.dat”,[formatter stringFromDate:selectedDate]];

//ファイルに保存する
[NSKeyedArchiver archiveRootObject:dataArray toFile:fileName];

//ファイルから復元する場合
[dataArray addObjectsFromArray:[NSKeyedUnarchiver unarchiveObjectWithFile:fileName]];

statusBar、navigationBar、tabBarの高さを取得

ステータスバーの高さ
float
statusbarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;

ナビゲーションバーの高さ
float
navigationbarHeight = self.navigationController.navigationBar.frame.size.height;

タブバーの高さ
float tabbarHeight = tabBarController.tabBar.frame.size.height;

CloudKitのデータベースで接続中のユーザーアカウントで作ったレコードを取得する

全体の流れ
1.接続中のユーザーのRecordIDを取得する
2.そのRecordIDを使ってクエリーを書く
3.クエリーを実行する

このまま書くとRecordIDが取得できる前に2が実行されてしまう。
1の部分はアプリ起動時等に先にやっておくのが良いと思う。

//接続中のユーザーのRecordIDを取得する
CKContainer*container=[CKContainer defaultContainer];
[container fetchUserRecordIDWithCompletionHandler:^(CKRecordID * recordID, NSError * error) {
    if(error==nil){
    NSLog(@”user fetch done”);
    self.userID=recordID;
}];

//そのRecordIDを使ってクエリーを書く
NSPredicate*predicate=[NSPredicate predicateWithFormat:@”creatorUserRecordID = %@”,[[CKReference alloc] initWithRecordID:recordID action:CKReferenceActionNone]];
CKQuery*query=[[CKQuery alloc]initWithRecordType:@”TargetRecords” predicate:predicate];

//クエリーを実行する
CKDatabase*publicDB=[CKContainer defaultContainer]publicCloudDatabase];
[publicDB performQuery:query inZoneWithID:nil completionHandler:^(NSArray*results,NSError*error){
    if(results.count>0){
        NSLog(@”userRecord fetch %lu”,(unsigned long)results.count);
    };
}];

CloudKitのデータベースで 現在接続中のUsersレコードを取得する

全体の流れ
1.データベースをセット
1.UsersレコードにアクセスできるOperation取得
2.レコード読み込み時のブロックをセット
4.Operation実行

//データベースをセット
CKDatabase*publicDB=[[CKContainer defaultContainer] publicCloudDatabase];

//UsersレコードにアクセスできるOperation取得
CKFetchRecordsOperation*operation=[CKFetchRecordsOperation fetchCurrentUserRecordOperation];

//レコード読み込み時のブロックをセット    operation.perRecordCompletionBlock=^(CKRecord*record,CKRecordID*recordID,NSError*error){
    if(error==nil){
         NSLog(@”user fetch done”);
         self.userID=recordID;
         self.user=record;
    }
};

//Operation実行
[publicDB addOperation:operation];

 

CloudKitで親子のテーブルのひも付け

全体の流れ
1.親のレコードからCKReferenceを得る
2.子レコード作る
3.子レコードのReferenceに入れる
4.子レコードを保存

親レコード:parentRecord
子レコードのタイプ:RecordType
子レコードにはCKReference型の”ref”カラムがあるとする。

//親レコードからCKReferenceを取得
CKReference*parentRef=[[CKReference alloc]initWithRecord:parentRecord action:CKReferenceActionDeleteSelf];

//子レコードを作る
CKRecord*newRecord=[CKRecord alloc]initWithRecordType:@”RecordType”];

//子レコードにCKReferenceをセット
[newRecord setValue:parentRef forKey:@”ref”];
…ここでその他のデータもセット

//保存
[publicDB saveRecord:record completionHandler:^(CKRecord*savedRecord,NSError*error){
}];

CoreDataのクエリーの書き方(NSPredicate,NSSortDescriptor)の書き方

全体の流れ
1.エンティティをセット
2.検索条件をセット(NSPredicate)
3.並び順をセット(NSSortDescriptor)
4.クエリー実行
5.CoreDataのクエリーだけでなく、CloudKitからのクエリーやNSArrayからの検索にも使える
6.NSPredicateは単純な比較もかけるし、predicateWithBlockを使えばかなり複雑なことも書ける

NSEntityDescription*entity = [NSEntityDescription entityForName:@”Item” inManagedObjectContext:self.context];
NSFetchRequest*req = [[NSFetchRequest alloc]init];

//リクエストにエンティティをセット
[req setEntity:entity];

//検索条件をセット
NSPredicate*predicate=[NSPredicate predicateWithFormat:@”registDate = %@”,selectedDate];
[req setPredicate:predicate];

//ソートを書く
NSSortDescriptor*sort1=[[NSSortDescriptor alloc]initWithKey:@”name” ascending:YES];
NSSortDescriptor*sort2=[[NSSortDescriptor alloc]initWithKey:@”registDate” ascending:YES];
NSArray*sortArray=@[sort1,sort2];
[req setSortDescriptors:sortArray];

//実行
NSArray*result=[self.context executeFetchRequest:req error:&error];


 

//中の要素をみながら比較する場合。条件に一致する場合はYESを返す
NSPredicate*predicate=[NSPredicate predicateWithBlock:^BOOL(id  record, NSDictionary * bindings) {
    if([record valueForKey:@”registDate”] == selectedDate){
        return YES;
    }else{
        return NO;
    }
}];

 

AutoLayoutの制約をコードで書く

全体の流れ
1.子になるViewを作る
2.親のViewに追加する
3.制約を書く
4.制約を追加する

親のViewにUIImageViewを追加してみる

UIImageView*imageView=[[UIImageView alloc]init];
imageView.translatesAutoresizingMaskIntoConstraints=NO;
[self.view addSubView:imageView];

NSMutableArray*array=[[NSMutableArray alloc]initWithCapacity:0];

//高さが300
[array addObject:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:300.0]];

//左は16開ける
[array addObject:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0 constant:16.0]];

//右も16開ける
[array addObject:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:-16.0]];

//上は100開ける
[array addObject:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:100.0]];

[self.view addConstraints:array];
[self.view layoutIfNeeded];

iOS8からは制約の追加は
[NSLayoutConstraint activateConstraints:array]
って書くらしい。
制約の追加先を考えなくてもいいのは楽かも。


センターを揃える時
[array addObject:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:100.0]];

アスペクト比を決める場合(この例は横が縦の半分)
[array addObject:[NSLayoutConstraint constraintWithItem:imageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:imageView attribute:NSLayoutAttributeHeight multiplier:1.0/2.0 constant:0.0]];

CloudKitで複数のアプリからデータベースを共有する

全体の流れ
1.App1とApp2からデータを共有するプラン
2.App1からは普通に接続
3.App2からはidを指定して接続

App1側
CKContainer*container=[CKContainer defaultContainer];

App2側
//App1のiCloudのidを指定する
CKContainer*container=[CKContainer containerWithIdentifier:@”xxx.iCloud.App1″];