月別アーカイブ: 2016年7月

【UIActivityIndicatorView】インジケーターの表示とか。

何かの処理中にインジケーターを表示して他の操作をさせなくしよう。

参考サイト
[iOS]アプリ内課金の金額をあらかじめ表示する – Qiita

まずUIviewを全画面に配置しautolayoutで全画面に入れます。
それに載せるようにUIActivityindicetorViewを配置します。
これはViewに対しX軸Y軸の中心になるようにautoLayoutします。

uiViewはBackGroundをBlackにし、Alpha値を0にしておきます。
UIActivityindicetorViewは、StyleをLarge Whiteにしておきます。

それらをoutlet接続します。

@property (weak, nonatomic) IBOutlet UIView *indicatorView;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *indicator;

それらを実行するメソッドを作成。
そのまんまコピーですみません。(−人−)

// indicator
- (void)indicator:(BOOL)start
{
    if (start) {
        // indicator start
        // 全面を覆うviewを表示
        [self.indicatorView setAlpha:0.6];
        // インジケータを回す
        [self.indicator startAnimating];
        // ステータスバーのインジケータも表示
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
        // (必要なら)バックボタンを消す
        [self.navigationItem setHidesBackButton:YES animated:YES];
    } else {
        // indicator stop
        // 全面を覆うviewを非表示にする
        [self.indicatorView setAlpha:0.0];
        // インジケータを止める
        [self.indicator stopAnimating];
        // ステータスバーのインジケータも消す
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        // (必要なら)バックボタンを表示する
        [self.navigationItem setHidesBackButton:NO animated:YES];
    }
}

インジケーターを表示したいところで

    // インジケータ表示
    [self indicator:YES];

インジケーターを終了したいところで

    // インジケータ非表示
    [self indicator:NO];

とゆー感じで使えるようになると思います。

【In-App Purchase】自分なりにアプリ内課金備忘録の2 リストア

自分用備忘録。
自分なりに整頓。

アプリ内課金のリストアについてはこちらが参考になった。
AT-Sphere: In-App Purchaseにハマる (リストア編)

冗長な感じだがまずはリストアボタンを押されてAlertで確認。

#pragma mark リストア
- (IBAction)restoreButton:(id)sender
{
    NSLog(@"リストアボタン押した");
    // コントローラを生成
    UIAlertController * ac =
    [UIAlertController alertControllerWithTitle:@"Restore"
                                        message:@"Do you want to start the restoration ?"
                                 preferredStyle:UIAlertControllerStyleAlert];
    
    // Cancel用のアクションを生成
    UIAlertAction * cancelAction =
    [UIAlertAction actionWithTitle:@"Cancel"
                             style:UIAlertActionStyleCancel
                           handler:^(UIAlertAction * action) {
                               // ボタンタップ時の処理
                               NSLog(@"Cancel button tapped.");
                           }];
    
    // OK用のアクションを生成
    UIAlertAction * okAction =
    [UIAlertAction actionWithTitle:@"OK"
                             style:UIAlertActionStyleDefault
                           handler:^(UIAlertAction * action) {
                               // ボタンタップ時の処理
                               [self startRestore];
                               NSLog(@"OK button tapped.");
                           }];
    
    // コントローラにアクションを追加
    [ac addAction:cancelAction];
    [ac addAction:okAction];
    
    // アラート表示処理
    [self presentViewController:ac animated:YES completion:nil];   
}

OKを押すと[self startRestore]でstartRestoreメソッドへ。
ココではプロダクトIDをセットして購入の確認に投げる。

- (void)startRestore
{
    NSLog(@"リストアスタート");
    
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
    //プロダクトIDをセット
    NSSet *set = [NSSet setWithObjects:@"プロダクトID2",@"プロダクトID3",@"プロダクトID4", nil];
    SKProductsRequest *prductsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    prductsRequest.delegate = self;
    // 購入履歴チェック
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

次のメソッドで購入済みプロダクトIDを確認してそれぞれ処理。

- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
    NSLog(@"ペイメントキューりすとあ");
    BOOL restore = NO;
    
    for (SKPaymentTransaction *transaction in queue.transactions)
    {
        // プロダクトIDが一致した場合
        if ([transaction.payment.productIdentifier isEqualToString:@"プロダクトID2"]) {
            restore = YES;
            NSLog(@"Restore1 OK!");
            
            [queue finishTransaction:transaction];
        }
        
        if ([transaction.payment.productIdentifier isEqualToString:@"プロダクトID3"]) {
            restore = YES;
            NSLog(@"Restore2 OK!");
            
            [queue finishTransaction:transaction];
        }
        
        if ([transaction.payment.productIdentifier isEqualToString:@"プロダクトID4"]) {
            restore = YES;
            NSLog(@"Restore3 OK!");
            
            [queue finishTransaction:transaction];
        }
    }
    
    // 一致するものがなかった場合
    if (restore == NO)
    {
        for (SKPaymentTransaction *transaction in queue.transactions) {
            [queue finishTransaction:transaction];
        }
        // コントローラを生成
        UIAlertController * ac =
        [UIAlertController alertControllerWithTitle:@"Error"
                                            message:@"Restre failed."//リストア失敗。
                                     preferredStyle:UIAlertControllerStyleAlert];
        
        // OK用のアクションを生成
        UIAlertAction * okAction =
        [UIAlertAction actionWithTitle:@"OK"
                                 style:UIAlertActionStyleDefault
                               handler:^(UIAlertAction * action) {
                                   // ボタンタップ時の処理
                                   NSLog(@"OK button tapped.");
                               }];
        
        // コントローラにアクションを追加
        [ac addAction:okAction];
        // アラート表示処理
        [self presentViewController:ac animated:YES completion:nil];
    }else{
        [self restoreSuccessAlart];
    }
}

最後にユーザーにリストア成功を伝えて完了。
あ、これはメソッド分けなくても良かったか。

-(void)restoreSuccessAlart
{
    UIAlertController * ac =
    [UIAlertController alertControllerWithTitle:@"Success"
                                        message:@"It succeeded to restore."
                                 preferredStyle:UIAlertControllerStyleAlert];
    
    // OK用のアクションを生成
    UIAlertAction * okAction =
    [UIAlertAction actionWithTitle:@"OK"
                             style:UIAlertActionStyleDefault
                           handler:^(UIAlertAction * action) {
                               // ボタンタップ時の処理
                               NSLog(@"OK button tapped.");
                           }];
    
    // コントローラにアクションを追加
    [ac addAction:okAction];
    // アラート表示処理
    [self presentViewController:ac animated:YES completion:nil];

}

あ、あと何らかの理由で失敗したら飛ぶらしいメソッド。よくわからん。

// リストアに失敗した場合
- (void) paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
    
    // コントローラを生成
    UIAlertController * ac =
    [UIAlertController alertControllerWithTitle:@"Error"
                                        message:@"Restre failed."//リストア失敗。
                                 preferredStyle:UIAlertControllerStyleAlert];
    
    // OK用のアクションを生成
    UIAlertAction * okAction =
    [UIAlertAction actionWithTitle:@"OK"
                             style:UIAlertActionStyleDefault
                           handler:^(UIAlertAction * action) {
                               // ボタンタップ時の処理
                               NSLog(@"OK button tapped.");
                           }];
    
    // コントローラにアクションを追加
    [ac addAction:okAction];
    // アラート表示処理
    [self presentViewController:ac animated:YES completion:nil];

}

こんな感じでどうですかね。
うじゃうじゃ。

【In-App Purchase】自分なりにアプリ内課金備忘録

アプリ内課金。
なんかめんどくさそう。
Appleのサーバとやりとりがどーたら。
ゆーててもしゃないので適当にやってみた。

参考サイト
iTunesConnect アプリ内課金 プロダクト作成 手順 – 散歩しながら 〜〜アプリ開発〜〜
失敗しない iOS In-App Purchase プログラミング – A Day In The Life
頭と尻尾はくれてやる! 初めてのアプリ内課金

課金コンテンツをAppleに預ける – How to implement "Hosting Content with Apple" – おおばログ

前提としてiTunes Connectのアプリで販売する物のプロダクトIDをつくっておく。
とりあえずココでは割愛。

で、まず、前処理としてアプリが課金可能な状態か確認する。

#pragma mark 購入前処理
-(void)canMakePayment
{
    if (![SKPaymentQueue canMakePayments])//購入制限がかかっている場合
    {
        
        // コントローラを生成
        UIAlertController * ac =
        [UIAlertController alertControllerWithTitle:@"Error"
                                            message:@"App billing are not allowed"
                                     preferredStyle:UIAlertControllerStyleAlert];
        
        // OK用のアクションを生成
        UIAlertAction * okAction =
        [UIAlertAction actionWithTitle:@"OK"
                                 style:UIAlertActionStyleDefault
                               handler:^(UIAlertAction * action) {
                                   // ボタンタップ時の処理
                                   NSLog(@"OK button tapped.");
                                   //購入制限がかかってたらなんかするときはココ
                               }];
        
        // コントローラにアクションを追加
        [ac addAction:okAction];
    }
}

続いて購入ボタンを押してプロダクトIDをセットして購入処理に入る。

#pragma mark 購入処理
- (IBAction)removeAdsButton:(id)sender {
    [self startPayment];
}

-(void)startPayment{
    //プロダクト情報のリクエストを開始。
    NSSet *set = [NSSet setWithObjects:@"ここに買うアイテムのプロダクトID", nil];
    SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    productsRequest.delegate = self;
    [productsRequest start];

}

プロダクトIDがセットされたらproductRequestに送られる。
よくわからんが、IDが間違ってないか確認してから支払い処理に投げるっぽい。

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    // 無効なアイテムがないかチェック
    if ([response.invalidProductIdentifiers count] > 0)
    {
        // コントローラを生成
        UIAlertController * ac =
        [UIAlertController alertControllerWithTitle:@"Error"
                                            message:@"item ID is not correct."//アイテムIDが不正です。
                                     preferredStyle:UIAlertControllerStyleAlert];
        
        // OK用のアクションを生成
        UIAlertAction * okAction =
        [UIAlertAction actionWithTitle:@"OK"
                                 style:UIAlertActionStyleDefault
                               handler:^(UIAlertAction * action) {
                                   // ボタンタップ時の処理
                                   NSLog(@"OK button tapped.");
                               }];
        
        // コントローラにアクションを追加
        [ac addAction:okAction];
        
    }else
    {
        // 購入処理開始
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        for (SKProduct *product in response.products)
            {
                SKPayment *payment = [SKPayment paymentWithProduct:product];
                [[SKPaymentQueue defaultQueue] addPayment:payment];
            }
    }
}

上記メソッドから自動で下記paymentQueueに投げられる。

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions) {
        if (transaction.transactionState == SKPaymentTransactionStatePurchasing) {
            // 購入処理中
            NSLog(@"購入処理中");
            /*
             * 基本何もしなくてよい。処理中であることがわかるようにインジケータをだすなど。
             */
            
        } else if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
            // 購入処理成功
            //ここでレシートの確認やアイテムの付与を行う。

            NSLog(@"広告の削除 %d",_removeAds);
            NSLog(@"購入処理成功");
            [queue finishTransaction:transaction];
            
        } else if (transaction.transactionState == SKPaymentTransactionStateFailed) {
            // 購入処理エラー。ユーザが購入処理をキャンセルした場合もここにくる
            [queue finishTransaction:transaction];
            NSLog(@"購入処理失敗");
            // エラーが発生したことをユーザに知らせる
            // コントローラを生成
            UIAlertController * ac =
            [UIAlertController alertControllerWithTitle:@"Error"
                                                message:[transaction.error localizedDescription]//アイテムIDが不正です。
                                         preferredStyle:UIAlertControllerStyleAlert];
            
            // OK用のアクションを生成
            UIAlertAction * okAction =
            [UIAlertAction actionWithTitle:@"OK"
                                     style:UIAlertActionStyleDefault
                                   handler:^(UIAlertAction * action) {
                                       // ボタンタップ時の処理
                                       NSLog(@"OK button tapped.");
                                   }];
            
            // コントローラにアクションを追加
            [ac addAction:okAction];
            

        } else {
            // リストア処理完了
            /*
             * アイテムの再付与を行う
             */
            [queue finishTransaction:transaction];
        }
    }		
}

うまく機能すれば支払いの確認のダイアログが出てOKすると支払い処理がなされる。

なんかリストア処理も入ってるっぽいので多分大丈夫じゃないかな。
複数の課金アイテムがあると処理がややこしそう。
自分用メモなので違ってたらゴメンね。
うじゃうじゃ。

【UITextView】でテキスト量などで高さを可変にする。

アプリの画面内の説明文で機種による画面のサイズの違いによる高さの違いを吸収したい。
普通はUILabelでするんだろうけど思惑があってUITextViewでやってみたかった。

Simulator Screen Shot 2016.07.01 13.58.02

Simulator Screen Shot 2016.07.01 9.44.48

次いで、autolayoutでUITextViewに連なるオブジェクトの位置が合わせて変わらなければ意味が無い。
いろいろと調べて試した結果意外と簡単だった。

参考サイトはコチラ。
UITableView上に可変するUITextViewを作る – Qiita
[iOS] AutoLayoutでUILabelの高さを動的に変える : 雑食プログラミング備忘録

まず、UITextViewを配置し、上と左右のautolayoutを設定する。
「高さが決まってないよ!」と注意が出るので、高さをテキストが入る余裕を持って設定する。

UITextViewの下端からマージンを取って下にUIButtonを設置しautolayoutで制約をつける。

そして、UITextViewのプロパティ、Scroll ViewのScrollingのScrolling Enabledのチェックを外す。

スクリーンショット 2016-07-01 13.40.42

その後、UITextViewの高さHeight ConstraintのRelationをLess Than or Equalに変更する。

スクリーンショット 2016-07-01 13.37.19

あと、編集も選択も出来ないようにしたいのでBehaviorのEditableとSelectableのチェックを外しておく。

こんなんで良いのかな。
うじゃうじゃ。