Help us understand the problem. What is going on with this article?

【どんなものでも】Objective-cで画像をギリギリまで切り抜いて色々な形にしよう【四角形にするよ!】

More than 5 years have passed since last update.

リストビューについて色々やっていたんですが、
うまーく正方形に切り抜いて表示したり、
長方形に切り抜いて表示したり...

調べて行っても中々上手くいかず
ちょっと苦戦しましたので、備忘録も兼ねて投稿します。

とりあえず、切り取り。 - 普通に正方形に切り抜く -

こいつを切ります。

Xmastree.jpg

真ん中に赤い飾りがあるから分かりやすいですね。

まず、画像を切るっていうのはどういうことなの?っていう話をしましょう。

手順1、まずは画像をプログラム的に認識させる。(UIImage)
手順2、表示域を設定する。(UIImageView)
手順3、その表示域の比に合うように画像を切る。(ここを紹介!)
手順4、表示域に画像乗っけて、表示する。(view addSubView: UIImageView)

まずは此方の潰れた画像の表示を御覧ください。

手順3を飛ばしたソース その1
    UIImage *Xmastree = [UIImage imageNamed:@"Xmastree.jpg"];

    CGRect rect = CGRectMake(0, 100, 300, 300);
    UIImageView *XmastreeAfter = [[UIImageView alloc]initWithFrame:rect];

    XmastreeAfter.image = Xmastree;
    [self.view addSubview:XmastreeAfter];

その1 実行結果
01.png

潰れてますね☆/(^O^)\フッジサーン
この実行結果に、手順3を挟むことで、予め正方形に切り取っておけば
いい感じに画像を表示させることが出来ると言う寸法です。

いま、300*300で表示域を用意しているので、
真ん中を中心にして、200*200で切って、それを300*300で表示させるときは
こんな感じです。

300*300でカット その2
    UIImage *Xmastree = [UIImage imageNamed:@"Xmastree.jpg"];

    int imageSize = 200;

    CGRect rect = CGRectMake(0, 100, 300, 300);
    CGRect cutRect = CGRectMake((Xmastree.size.width - imageSize)/2, (Xmastree.size.height  - imageSize)/2, imageSize, imageSize);

    CGImageRef clip = CGImageCreateWithImageInRect(Xmastree.CGImage,cutRect);
    UIImage *clipedImage = [UIImage imageWithCGImage:clip];
    CGImageRelease(clip);

    UIImageView *XmastreeAfter = [[UIImageView alloc]initWithFrame:rect];
    XmastreeAfter.image = clipedImage;

    [self.view addSubview:XmastreeAfter];

その2 実行結果
02.png

CGImageCreateWithImageInRect

これは任意x,y座標からの範囲から画像を取得するやつです。
これを使って正方形に切り抜いたイメージを、UIViewImageに突っ込むと、
上の写真のようになります。

出来ればギリギリまで使いたい! - 横いっぱいに正方形に切り抜く -

まず、ぎりぎりまで使いたい。
と考えた時に懸念点としてあるのが
「画像の幅を超えたサイズの指定」だと思います。
意外と上手く切り取れないんですよね....

というわけで、こんな感じでどうでしょうか。

精一杯正方形にカット その3
    UIImage *Xmastree = [UIImage imageNamed:@"Xmastree.jpg"];

    CGRect rect = CGRectMake(0, 100, 300, 300);
    CGRect cutRect;

    float imageMaxSize;

    if (Xmastree.size.width >= Xmastree.size.height) {
        imageMaxSize = Xmastree.size.height;
        cutRect = CGRectMake((Xmastree.size.width - imageMaxSize)/2, 0, imageMaxSize, imageMaxSize);
    } else {
        imageMaxSize = Xmastree.size.width;
        cutRect = CGRectMake(0, (Xmastree.size.height  - imageMaxSize)/2, imageMaxSize, imageMaxSize);
    }

    CGImageRef clip = CGImageCreateWithImageInRect(Xmastree.CGImage,cutRect);
    UIImage *clipedImage = [UIImage imageWithCGImage:clip];
    CGImageRelease(clip);



    UIImageView *XmastreeAfter = [[UIImageView alloc]initWithFrame:rect];
    XmastreeAfter.image = clipedImage;

    [self.view addSubview:XmastreeAfter];

ここで気をつけるのは、画像サイズの横幅と縦幅、どっちが大きいの?
という点だけです。

今回は縦長で、横 < 縦 ですね。
ということで、横いっぱいを画像の最大値とすれば、

その3 実行結果
03.png

こんな感じで表示できますね!

気をつけろ! 表示倍率トラップ

さて、これで切り抜きなんてもう余裕だぜ!
なんて考えてると、ドツボにはまるのが倍率の問題。

今xcodeのpathはXmastree.jpgのみ!というように
用意した画像が一枚で、それしか利用しないって時はきにしなくていいんですけど

Retina対応して物によって画像を変えよう!と思った時。
例えば
Xmastree.jpg 160*213
Xmastree@x2.jpg 320*427
Xmastree@x3.jpg 480*640

という感じでパスを通すと

UIImage *Xmastree = [UIImage imageNamed:@"Xmastree.jpg"];

と書いてあるのでXmastree.size.widthの値は160になります。
のに、画像を表示させるときに利用される画像は各機種に対応した画像になります。
ex)iPhone5なら320*427の画像を表示するが、取得した最大横幅160となる。2倍のズレが生じる。
つまり取りたいところがずれっずれになります。
なので

float scale = Xmastree.scale;

画像を切り取る上でこれが大事になってきます。
iPhone4,5の時scaleの値 2
iPhone6の時scaleの値 3
となります。これをいい感じに利用して

精一杯画像幅に気を付けて切り抜き その4
    UIImage *Xmastree = [UIImage imageNamed:@"Xmastree.jpg"];

    CGRect rect = CGRectMake(0, 100, 300, 300);
    CGRect cutRect;

    float imageMaxSize;
    float scale = Xmastree.scale;

    if (Xmastree.size.width >= Xmastree.size.height) {
        imageMaxSize = Xmastree.size.height;
        cutRect = CGRectMake((Xmastree.size.width - imageMaxSize)/2 * scale, 0, imageMaxSize * scale, imageMaxSize * scale);
    } else {
        imageMaxSize = Xmastree.size.width;
        cutRect = CGRectMake(0, (Xmastree.size.height  - imageMaxSize)/2 * scale, imageMaxSize * scale, imageMaxSize * scale);
    }

    CGImageRef clip = CGImageCreateWithImageInRect(Xmastree.CGImage,cutRect);
    UIImage *clipedImage = [UIImage imageWithCGImage:clip];
    CGImageRelease(clip);

    UIImageView *XmastreeAfter = [[UIImageView alloc]initWithFrame:rect];
    XmastreeAfter.image = clipedImage;

    [self.view addSubview:XmastreeAfter];

これでRetina対応するために複数画像用意してもその3の表示結果と同じになります。

これでどんな画像を使おうとも安心ですね。

正方形はわかったけど、長方形は? - 比率を守りつつ最大限に切り抜く -

所謂グノシーUIの一番上のセルのように、
画像を引っ張ってきて、それを長方形に切るにはどうしたらいいの?

という疑問を解決できたらいいなぁ(遠い目)

といっても、長方形だからといって気をつける点は横幅超えないように気をつけるのと
比率を守るということだけです。

まずは此方をどうぞ。

精一杯長方形に切り抜き その5
    UIImage *Xmastree = [UIImage imageNamed:@"Xmastree.jpg"];

    CGRect rect = CGRectMake(0, 100, 300, 200);
    CGRect cutRect;

    float imageMaxSize;
    float scale = Xmastree.scale;

    float ratio = rect.size.width / rect.size.height;

    if (Xmastree.size.width >= Xmastree.size.height) {
        imageMaxSize = Xmastree.size.height;
    } else {
        imageMaxSize = Xmastree.size.width;
    }

    cutRect = CGRectMake((Xmastree.size.width -
                          imageMaxSize)/2 * scale,
                         (Xmastree.size.height -
                          (imageMaxSize / ratio))/2 * scale,
                         imageMaxSize * scale,
                         imageMaxSize / ratio * scale
                         );

    CGImageRef clip = CGImageCreateWithImageInRect(Xmastree.CGImage,cutRect);
    UIImage *clipedImage = [UIImage imageWithCGImage:clip];
    CGImageRelease(clip);

    UIImageView *XmastreeAfter = [[UIImageView alloc]initWithFrame:rect];
    XmastreeAfter.image = clipedImage;

    [self.view addSubview:XmastreeAfter];

その5 実行結果
04.png

精一杯の長方形を作るときの考え方は、
そんなに難しく有りません。

そもそも、1辺の最大値が同じなら面積的に
正方形>長方形 になるのは自明です。

なら、途中までは正方形を作る気分と一緒です。

大事なのは、比率です。

float ratio = rect.size.width / rect.size.height;

これは、表示域の横 / 縦ですね。これを利用して、比率を担保しています。
ちょっとごちゃごちゃして分かりにくいソースで申し訳ないのですが、
これで表示域の比率と同値の長方形に切り抜き出すことが出来ます。

あとがき

どうでしょう。
一度正方形に切り出してしまえれば
後はどうにでも出来そうだというのが伝われば幸いです。

ソースは僕が勉強でがりがり書いたものをそのままコピペしているので
ちょっと、いや、それなりに整理が成ってません。

編集リク、お待ちしております(爆)。

それでは。

mmusasabi
(*´д`*)パッション!!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away