web-dev-qa-db-ja.com

UILabelレイヤーcornerRadiusはパフォーマンスに悪影響を及ぼします

コーナーにページ番号を表示するドキュメントビューを作成しました。ページ番号は、半透明の背景色のUilabelであり、角の半径があります(cornerRadiusviewlayerプロパティを使用)。これをUIScrollViewの上に配置しました。ただし、これによりスクロールがぎくしゃくします。 cornerRadiusを削除すると、パフォーマンスは良好です。これについて何かできることはありますか?より良い解決策は何ですか? UIWebViewでパフォーマンスの問題なしで達成されたようです。

45
Nick

ラベル、または角が丸いビュー、またはスクロールビューの背景色と影の場合、ソリューションは非常に簡単です。

最大の問題は、masksToBoundsレイヤーオプションにあります。これはパフォーマンスを著しく低下させるように見えますが、ラベルは丸みを帯びた角に背景色を隠すためにこのONを必要とするようです。したがって、これを回避するには、代わりにラベルレイヤーの背景色を設定し、masksToBoundsをオフにする必要があります。

2番目の問題は、デフォルトの動作では、可能な場合は常にビューを再描画することです。これは、スクロールビューの静的または低速の変更アイテムではまったく不要です。ここでは、layer.shouldRasterize = YESを設定するだけです。これにより、CAはスクロール時の高速描画のために、ラスタライズバージョンのビューを「キャッシュ」できます(おそらくハードウェアアクセラレーションを使用)。

レイヤーにアルファチャンネルがあることを確認する必要があります。そうでない場合、ラスタライズは角丸の描画に影響します。背景色にアルファを設定しているので問題はありませんでしたが、状況を確認する必要があるかもしれません。

以下は、scollviewでうまく動作するように設定されたUILabelのサンプルです。

UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(4, 4, 40.0, 24.0)];
lbl.font = [UIFont fontWithName:@"Helvetica" size:14.0];
lbl.textAlignment = UITextAlignmentRight;
lbl.text = @"Hello World";
// Must set the label background to clear so the layer background shows
lbl.backgroundColor = [UIColor clearColor];        
// Set UILabel.layer.backgroundColor not UILabel.backgroundColor otherwise the background is not masked to the rounded border.
lbl.layer.backgroundColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.5].CGColor;

lbl.layer.cornerRadius = 8;
lbl.layer.borderColor = [UIColor blackColor].CGColor;
lbl.layer.borderWidth = 1;
// Huge change in performance by explicitly setting the below (even though default is supposedly NO)
lbl.layer.masksToBounds = NO;
// Performance improvement here depends on the size of your view
lbl.layer.shouldRasterize = YES;
lbl.layer.rasterizationScale = [UIScreen mainScreen].scale;
// self here is the child view in the scroll view
[self addSubview:lbl];
[lbl release];

IPad 1の画面をこのようなビューで埋めても、スムーズにスクロールできます:)

145
psychoacoustic

私もこれにつまずいた。 cornerRadius(UIButtonおよびUIImageView用)がアプリのパフォーマンスを低下させていました。 UIButtonを背景画像で丸める実用的なソリューションが見つからなかったため、独自のコードを作成する必要がありました-最初の方法。 2番目は、UIImageViewに丸い角と境界線を追加します。コードはそれ自体を物語っていると思います。

+(void)setBackgroundImage:(UIImage*)image forButton:(UIButton*)button withCornerRadius:(float)cornerRadius borderColor:(CGColorRef)borderColor andBorderWith:(float)borderWidth
{
    UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:button.frame];
    UIGraphicsBeginImageContextWithOptions(tempImageView.bounds.size, NO, 1.0);
    [[UIBezierPath bezierPathWithRoundedRect:tempImageView.bounds
                                cornerRadius:cornerRadius] addClip];
    [image drawInRect:tempImageView.bounds];
    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [button setBackgroundImage:tempImageView.image forState:UIControlStateNormal];
    button.layer.shouldRasterize = YES;
    button.layer.borderColor = borderColor;
    button.layer.borderWidth = borderWidth;
    button.layer.cornerRadius = cornerRadius;
}

+(void)makeRoundedCornersForImageView:(UIImageView*)imgV withCornerRadius:(float)cornerRadius borderColor:(CGColorRef)borderColor andBorderWith:(float)borderWidth
{
    UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:imgV.frame];
    UIGraphicsBeginImageContextWithOptions(tempImageView.bounds.size, NO, 1.0);
    [[UIBezierPath bezierPathWithRoundedRect:tempImageView.bounds
                                cornerRadius:cornerRadius] addClip];
    [imgV.image drawInRect:tempImageView.bounds];
    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    imgV.image = tempImageView.image;
    imgV.layer.shouldRasterize = YES;
    imgV.layer.borderColor = borderColor;
    imgV.layer.borderWidth = borderWidth;
    imgV.layer.cornerRadius = cornerRadius;
}
8
Szuwar_Jr

Petertが提案したように、サイドバーの「関連する」投稿を見た後、自分の画像を作成することにしました。 UIViewをサブクラス化し、背景画像(エッジが丸い背景用)とinitの標準テキストラベルを追加しました。背景画像を作成するには、CG描画機能を使用して伸縮可能な画像を作成します。

    // create background image
    CGFloat radius = frame.size.height / 2;
    CGFloat imgSize = (radius*2)+1; // 1 pixel for stretching
    UIGraphicsBeginImageContext(CGSizeMake(imgSize, imgSize));
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetAlpha(context, 0.5f);
    CGContextSetLineWidth(context, 0);
    CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);

    CGFloat minx = 0;
    CGFloat midx = imgSize/2;
    CGFloat maxx = imgSize;
    CGFloat miny = 0;
    CGFloat midy = imgSize/2;
    CGFloat maxy = imgSize;

    CGContextMoveToPoint(context, minx, midy);
    CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
    CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
    CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
    CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke);

    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIImage *stretchImage = [viewImage stretchableImageWithLeftCapWidth:radius topCapHeight:radius];

    UIImageView *stretch = [[UIImageView alloc] initWithImage:stretchImage];
    stretch.frame = self.bounds;
    stretch.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
    [self addSubview:stretch];
    [self sendSubviewToBack:stretch];
    [stretch release];
6
Nick

角が丸いカスタムUILabelUITableViewCellでも同様の問題がありました。スムーズなパフォーマンスを得るために、角を丸くしたイメージを「作成」し、これを回避しました( see )。

thisthis など、他の多くの投稿が役立つ場合があります。

3
petert