*

iOSプログラミングのキモ(複雑な画面を複数のViewControllerで制御する その2)

公開日: : Apple, iOS, iPhone, Objective-C, XCode

先週は、複数のViewControllerで1つの画面を構成する話のうち、親ViewControllerについて書きました。

少しおさらいすると、キモは 親viewDidLoadメソッドの中で 子供viewController をインスタンス化して 子供のrootViewを
親rootViewにaddSubViewすることで 親画面に組み込まれます。

親viewControllerと子viewControllerの間で情報のやりとりですが、密なやりとりが発生する場合は NSNotification を使ったり、
delegate を使ったりすることで 実現させたりしますが。今回は エンティティクラスを定義して そのインスタンスを子ViewControllerに渡しています。

今回使っているエンティティクラスは以下の様なものです。

JSONModel.h

#import "JSONModel.h"

#import "VideoLink.h"
#import "MediaThumbnail.h"

@interface VideoModel : JSONModel
{
    NSString *videoId_;
    NSString *authorName_;
    NSString *authorUri_;
    NSDate *published_;
    NSString *description_;
    int viewCount_;
    float ratingAverage_;

}

@property (strong, nonatomic) NSString* title;
@property (assign, nonatomic) int duration;
@property (strong, nonatomic) NSArray* link;
@property (strong, nonatomic) NSArray* thumbnail;

-(NSString*)videoId;
-(NSString*)authorName;
-(NSString*)authorUri;
-(NSDate*)published;
-(int)numDislikes;
-(int)numLikes;
-(int)favoriteCount;
-(int)viewCount;
-(NSString*)description;
-(float)ratingAverage;
-(void)setVideoId:(NSString*)videoId;

-(void)setAuthorName:(NSString*)authorName;
-(void)setAuthorUri:(NSString*)authorUri;
-(void)setDescription:(NSString*)description;
-(void)setPublished:(NSDate*)published;
-(void)setViewCount:(int)viewCount;
-(void)setRatingAverage:(float)ratingAverage;
@end

JSONModel.m

#import "VideoModel.h"

@implementation VideoModel

+(JSONKeyMapper*)keyMapper
{
    return [[JSONKeyMapper alloc] initWithDictionary:@{
            @"media$group.media$thumbnail":@"thumbnail",
            @"title.$t": @"title",
            @"media$group.yt$duration.seconds":@"duration",
            }];
}

-(void)setAuthorName:(NSString*)authorName
{
    authorName_ = authorName;
}

-(NSString*)authorName
{
    if(authorName_)
        return authorName_;
    return [self.__dic__[@"author"] objectAtIndex:0][@"name"][@"$t"];
}

-(void)setAuthorUri:(NSString*)authorUri
{
    authorUri_ = authorUri;
}

-(NSString*)authorUri
{
    if(authorUri_)
        return authorUri_;
    return [self.__dic__[@"author"] objectAtIndex:0][@"uri"][@"$t"];
}

-(void)setPublished:(NSDate*)published;
{
    published_ = published;
}

-(NSDate*)published
{
    if(published_)
        return published_;

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
    // example: 2009-11-04T19:46:20.192723
    [dateFormatter setDateFormat:@"yyyy-MM-dd'T'H:mm:ss'.000Z'"];

    NSString *dateStr = self.__dic__[@"published"][@"$t"];
    //LOG(@"dateStr = %@",dateStr);
    return [dateFormatter dateFromString:dateStr];
}

-(int)numDislikes
{
    NSString *str = self.__dic__[@"yt$rating"][@"numDislikes"];
    return [str intValue];
}

-(int)numLikes
{
    //LOG(@"%@",self.__dic__);
    NSString *str = self.__dic__[@"yt$rating"][@"numLikes"];
    return [str intValue];
}

-(int)favoriteCount
{
    NSString *str = self.__dic__[@"yt$statistics"][@"favoriteCount"];
    return [str intValue];
}

-(void)setViewCount:(int)viewCount
{
    viewCount_ = viewCount;
}

-(int)viewCount
{
    if(viewCount_ > 0)
        return viewCount_;

    NSString *str = self.__dic__[@"yt$statistics"][@"viewCount"];
    return [str intValue];
}

-(void)setRatingAverage:(float)ratingAverage
{
    ratingAverage_ = ratingAverage;
}

-(float)ratingAverage
{
    if(ratingAverage_)
        return ratingAverage_;

    NSString *str = self.__dic__[@"gd$rating"][@"average"];
    return [str floatValue];
}

-(void)setDescription:(NSString*)description;
{
    description_ = description;
}

-(NSString*)description
{
    if(description_)
        return description_;

    return self.__dic__[@"media$group"][@"media$description"][@"$t"];
}

-(void)setVideoId:(NSString*)videoId
{
    videoId_ = videoId;
}

-(NSString*)videoId
{
    if(videoId_)
        return videoId_;

    VideoLink* link = self.link[0];

    NSString* videoId = nil;
    // LOG(@"%@#videoId link.href.query=%@", [self class], link.href.query);
    NSArray *queryComponents = [link.href.query componentsSeparatedByString:@"&"];
    if(queryComponents==nil || [queryComponents count]==0){
        NSDictionary *dic = [self toDictionary];

        // LOG(@"%@#videoId [dic debugDescription]=%@",[self class], [dic debugDescription]);

        NSArray *array = (NSArray*)[dic objectForKey:@"link"];
        // LOG(@"%@#videoId [array debugDescription]=%@",[self class], [array debugDescription]);

        NSDictionary *dic2 = [array objectAtIndex:1];
        // LOG(@"%@#videoId [dic2 debugDescription]=%@",[self class], [dic2 debugDescription]);

        NSString *link0 = [dic2 objectForKey:@"href"];
        // LOG(@"%@#videoId link0=%@",[self class], link0);

        queryComponents = [link0 componentsSeparatedByString:@"&"];
    }

    for (NSString* pair in queryComponents) {
        // LOG(@"%@#videoId pair=%@",[self class], pair);
        NSRange range = [pair rangeOfString:@"v="];
        if (range.location != NSNotFound) {
            NSArray* pairComponents = [pair componentsSeparatedByString:@"="];
            videoId = pairComponents[1];
            break;
        }
    }

    // LOG(@"%@#videoId videoId=%@",[self class], videoId);
    videoId_ = videoId;
    return videoId;
}

@end

VideoModelは、YouTubeに登録されている主なメタ情報を収録しているエンティティクラスです。この情報を子ViewControllerに渡すことで 子ViewControllerは 自分のやるべき処理が出来るようになります。

子ViewControllerの動作タイミングは、親ViewControllerと同じにしたいことが多いですが、子viewControllerのviewWillAppear,viewDidAppear,viewWillDisappear,viewDidDisappear は 自発的に呼んであげる必要があります。
そこで、対策として 親viewControllerの viewWillAppear,viewDidAppear,viewWillDisappear,viewDidDisappear の中で 子viewControllerの同じメソッドを呼ぶようにします。

今回は 子ViewControllerの1つInfoViewControllerについて解説します。

IMG_1351

InfoViewControllerは 再生中動画のメタ情報を表示します。メタ情報の表示手法ですが、UIWebVIewを使いHTMLで表示しています。

InfoViewController.h

//
//  InfoViewController.h
//  QTubeTestBrowser
//
//  Created by Matsuda Katsumi on 2013/08/12.
//  Copyright (c) 2013年 LISONAL ltd. All rights reserved.
//

#import <UIKit/UIKit.h>

@class VideoModel;
@class WebVideoViewController;

@interface InfoViewController : UIViewController<UIWebViewDelegate>

@property (weak,nonatomic) VideoModel *video;
@property (weak,nonatomic) WebVideoViewController *webVideoViewController;

-(void)setFrame:(CGRect)frame;

@end

InfoViewController.m

//
//  InfoViewController.m
//  QTubeTestBrowser
//
//  Created by Matsuda Katsumi on 2013/08/12.
//  Copyright (c) 2013年 LISONAL ltd. All rights reserved.
//

#import "InfoViewController.h"
#import "VideoModel.h"
#import "WebVideoViewController.h"

@interface InfoViewController ()
{
    IBOutlet UIWebView *webView_;
}

@end

@implementation InfoViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    webView_.delegate = self;

    float ratingAverage = [self.video ratingAverage]*20.0f;
    float ratingAveragePercent = ratingAverage/100.0;
    NSString *description = [[self.video description] stringByReplacingOccurrencesOfString:@"\n" withString:@"<br/>"];

    NSDateFormatter *outputDateFormatter = [[NSDateFormatter alloc] init];
	NSString *outputDateFormatterStr = @"yyyy-MM-dd";
	//[outputDateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"JST"]];
	[outputDateFormatter setDateFormat:outputDateFormatterStr];
	NSString *outputDateStr = [outputDateFormatter stringFromDate:[self.video published]];

    NSString *viewCountStr = [AppDelegate createStringAddedCommaFromInt:[self.video viewCount]];

    NSError *error = nil;
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"VideoInfo.JP" ofType:@"html"];
    NSString *string = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];

    NSString *html = [NSString stringWithFormat:string, [self.video title], [self.video authorName], [[self.video title] stringByRemovingNewLinesAndWhitespace], viewCountStr, outputDateStr,ratingAverage,ratingAveragePercent,description];
    // LOG(@"%@", html);
    NSURL *url = [NSURL URLWithString:[[NSBundle mainBundle]pathForResource:@"VideoInfo.JP" ofType:@"html"]];

    [webView_ loadHTMLString:html baseURL:url];
    //if(webView_)
    // LOG(@"%@",string);

}

-(void)viewDidAppear:(BOOL)animated
{
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    // LOG(@"%@", [error description]);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)setFrame:(CGRect)frame
{
    self.view.frame = frame;
    LOG(@"self.view.frame = %@", NSStringFromCGRect(self.view.frame));
}

// 読み込みインジケーター表示
- (void)webViewDidStartLoad:(UIWebView *)webView{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

// リンクをクリック時、Safariを起動する為の処理
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    LOG(@"[request URL]=%@", [request URL]);
    if (navigationType == UIWebViewNavigationTypeLinkClicked || navigationType == UIWebViewNavigationTypeOther)
    {
        NSString* scheme = [[request URL] scheme];
        if([scheme compare:@"http"] == NSOrderedSame || [scheme compare:@"https"] == NSOrderedSame || [scheme compare:@"itms"] == NSOrderedSame) {
            [[UIApplication sharedApplication] openURL: [request URL]];
            return NO;
        }
        if([scheme compare:@"uiactivity"] == NSOrderedSame) {
            [self.webVideoViewController displayUIActivityView];
            return NO;
        }
    }
    return YES;
}

@end

表示用のHTMLは 以下の様になっています。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
    <style type="text/css">
        * {
            word-break: break-all;
            -webkit-touch-callout:none;
            -webkit-tap-highlight-color:rgba(255,255,0,0.4);
            -webkit-text-size-adjust:none;
        }
        body {
            color: #000000;
            font-size: 15px;
            line-height: 1.3;
            font-family: ArialMT, "Hiragino Kaku Gothic ProN", "ヒラギノ角ゴ ProN W3"; /* iPhoneバンドル */
            -webkit-text-size-adjust: none; /* 文字の拡大縮小を防ぐ */
        }
        h1{
            color: #000000;
            font-size: 16px;
        }
        h2{
            color: #FF0000;
            font-size: 15px;
        }
        p{
            color: #000000;
            font-size: 15px;
        }
        </style>
</head>
<body>
    <h1>%@</h1>
    <div style="color:#ff0000;">
        <image src="user.png" />&nbsp;%@&nbsp;&nbsp;&nbsp;&nbsp;<a href="itms://search.itunes.apple.com/WebObjects/MZSearch.woa/wa/search?term=%@"><img src="Download_on_iTunes_Badge_US-UK_110x40_1004.png" alt="iTunes" width="55" height="20" /></a>&nbsp;&nbsp;&nbsp;&nbsp;
        <a href="uiactivity://calluiactivity"><img src="share.png" alt="share" /></a>
    </div>
    <div><image src="play1.png" /> %@&nbsp;&nbsp;<image src="publish.png" />&nbsp;%@</div>
    <div style="clear:left;"><image src="like.png" />&nbsp;%.0f%%</div>
    <canvas id="a_canvas" width="300" height="10"></canvas>
    <script type="text/javascript">
        // コンテキストを取得
        var a_canvas = document.getElementById("a_canvas");
        var context = a_canvas.getContext("2d");
        // 背景を描画
        var gradient = context.createLinearGradient(0,0,0,300);
        gradient.addColorStop(0, "#e0e0e0");
        gradient.addColorStop(1, "#ffffff");
        context.fillStyle = gradient;
        context.fillRect(0,0,a_canvas.width,a_canvas.height);

        // 枠線を描画
        context.lineWidth = 20;
        context.strokeStyle = "#ff0000";
        // 枠線を描画
        context.beginPath();
        context.moveTo(0,0);
        context.lineTo(a_canvas.width*%.2f,0);
        context.stroke();
        </script>

    <hr/>
    <div>%@</div>
</body>
</html>

HTMLのソースを見ていくと、%@ と書かれている箇所があります。
viewDidLoadメソッドの中で、%@ に所定の情報を埋め込み 最終的なHTMLに仕上げています。

NSError *error = nil;
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"VideoInfo.JP" ofType:@"html"];
    NSString *string = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];

    NSString *html = [NSString stringWithFormat:string, [self.video title], [self.video authorName], [[self.video title] stringByRemovingNewLinesAndWhitespace], viewCountStr, outputDateStr,ratingAverage,ratingAveragePercent,description];

NSURL *url = [NSURL URLWithString:[[NSBundle mainBundle]pathForResource:@"VideoInfo.JP" ofType:@"html"]];
    [webView_ loadHTMLString:html baseURL:url];

今日のまとめ

この画面では、親ViewControllerと頻繁にコミュニケーションを行う必要がないことと、表示自体はUIWebViewに任せているので Objective-Cのソースは簡潔になっています。

  1. 親と子の間で エンティティクラスを介して 情報をしよう。
  2. 動作タイミングを合わせる場合、親のviewWillAppear,viewDidAppear,viewWillDisappear,viewDidDisappear に中で 同じメソッドを呼んであげよう。
  3. ヘルプ画面や解説画面を作る場合、UIWebViewを使ってみよう

 

関連記事

iOSプログラミングのキモ(行き当たりばったりなプログラミングでも、何とか形にするために守っていること その1)

FileQ iOS版がリリースされて、のんびりしたい気持ちもありますが、FileQ Android版

記事を読む

iOSプログラミングのキモ(AppDelegate説明 iPhone5以降とiPhone4S以前を画面サイズで判別する )

iPhone5以降 画面サイズが2パターン(iPhone6ではさらに増えるかも・・(^^;))になっ

記事を読む

iPhoneが7GBの転送制限に引っかかりそうになって Nexus7(2013)LTE版のテザリング機能が大活躍

皆さんは スマホを使ってて、転送制限に引っかかったことはありますか?私はあります(笑) 最

記事を読む

iOSプログラミングのキモ(行き当たりばったりなプログラミングでも、何とか形にするために守っていること その2)

先週に引き続き、今週も文字中心のエントリーです、今回は 下記3つのことを書いていきます。 M

記事を読む

iOSプログラミングのキモ(2:ソースコード概説 )主要なObjective-Cソース・ファイル一覧

QTubeの主要Objective-Cのソース一覧です。 iOSでは アプリを作る場合 Objec

記事を読む

iOSプログラミングのキモ(iOS7から使えるようになったマルチタスク機能、NSURLSessionはこう使え!)

今日はQTubeのソースに関する話題ではなく、現在開発中のアプリで使っているNSURLSession

記事を読む

iOSプログラミングのキモ(デバッグをやりやすくするための工夫:コンソール・ログの出し方 )

iOS上でプログラミングをする時、ログ出力用の関数としてNSLogという関数をよく使います。NSLo

記事を読む

iOSプログラミングのキモ(拡張子がpchというファイルの役目)

XCodeで プロジェクトを作成すると、-prefix.pch というファイルができています。このフ

記事を読む

iOSプログラミングのキモ(Delegate iOSプログラミングで避けて通れないしくみ)

Delegate(委任)の考え方を説明します。iOSのプログラミングでは このDelegateが頻繁

記事を読む

iOSプログラミングのキモ

このブログでは、実際に弊社が公開しているアプリのソースコードを使って、iOSプログラミングのキモを解

記事を読む

Comment

  1. […] iOSプログラミングのキモ(複雑な画面を複数のViewControllerで制御する … […]

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

FileQ Hosting 月額99円 容量1GB


サイト管理 Mezzanine
Django上で動くCMS Mezzanine 用のモジュールを作ってみる その1

Django上で動くCMS Mezzanine上で動く、モジュールを作

ホーム Mezzanine
Django上で動くCMS Mezzanine を インストールする MacOSX Yesemite 編

Mezzanineは Django WEBフレームワーク上で動くCMS

EclipseにGWT(Google Web Toolkit) Plugin for Eclipseを入れようとしてハマった

最近PHPでちょっとした業務システムを作りました。業務システムの特徴と

ブログを半年やった成果を Google Analytics から眺める

今年の1月からブログを書き始め、そろそろ半年が経とうとしています。

母校で特別 講義をやってきました。

少し 間が空いてしまいました(^_^;) ちょっと前になりますが

→もっと見る

mautic is open source marketing automation
PAGE TOP ↑