69

素敵なグラデーションの背景を作成するために使用しているCAGradientLayersを取得しようとしていますが、回転とモーダルビューのプレゼンテーションで適切にサイズ変更されますが、ボールをプレーしません。

これは私が作成したビデオで、私の問題を示しています。

また、このビデオは、OS X で iPhone シミュレーターを撮影して作成されたことに注意してください。問題を強調するために、ビデオのアニメーションの速度を落としました。

問題のビデオ...

これは私が作成したばかりの Xcode プロジェクトです (これは、ビデオに表示されているアプリのソースです)。

Xcode プロジェクト、CAGradientLayer 背景を使用してビューをモーダルに表示...

価値があるのは、次を使用することを理解しています:

    [[self view] setBackgroundColor:[UIColor blackColor]];

トランジションをもう少しシームレスにして不快感を少なくするという合理的な仕事をしていますが、現在横向きモードでビューをモーダルに表示しているときにビデオを見ると、上記のコードが役に立たない理由がわかります.

これを整理するために私にできることはありますか?

ジョン

4

10 に答える 10

183

レイヤー (グラデーション レイヤーなど) を作成する場合、レイヤーを管理するビューはありません (ビューのレイヤーのサブレイヤーとして追加する場合でも)。UIViewこのようなスタンドアロン レイヤは、アニメーション システムに参加しません。

そのため、グラデーション レイヤーのフレームを更新すると、レイヤーは独自のデフォルト アニメーション パラメータを使用して変更をアニメーション化します。(これは「暗黙のアニメーション」と呼ばれます。) これらの既定のパラメーターは、インターフェイスの回転に使用されるアニメーションのパラメーターと一致しないため、奇妙な結果が得られます。

私はあなたのプロジェクトを見ていませんでしたが、このコードで問題を再現するのは簡単です:

@interface ViewController ()

@property (nonatomic, strong) CAGradientLayer *gradientLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.gradientLayer = [CAGradientLayer layer];
    self.gradientLayer.colors = @[ (__bridge id)[UIColor blueColor].CGColor, (__bridge id)[UIColor blackColor].CGColor ];
    [self.view.layer addSublayer:self.gradientLayer];
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.gradientLayer.frame = self.view.bounds;
}

@end

シミュレーターでスローモーションを有効にすると、次のようになります。

回転が悪い

幸いなことに、これは簡単に修正できる問題です。グラデーション レイヤーをビューで管理する必要があります。これを行うには、をレイヤーとしてUIView使用するサブクラスを作成します。CAGradientLayerコードは小さいです:

// GradientView.h

@interface GradientView : UIView

@property (nonatomic, strong, readonly) CAGradientLayer *layer;

@end

// GradientView.m

@implementation GradientView

@dynamic layer;

+ (Class)layerClass {
    return [CAGradientLayer class];
}

@end

GradientView次に、コードをの代わりに使用するように変更する必要がありますCAGradientLayer。レイヤーの代わりにビューを使用しているため、自動サイズ変更マスクを設定して、グラデーションのサイズをそのスーパービューに維持できるため、後で回転を処理するために何もする必要はありません。

@interface ViewController ()

@property (nonatomic, strong) GradientView *gradientView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.gradientView = [[GradientView alloc] initWithFrame:self.view.bounds];
    self.gradientView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    self.gradientView.layer.colors = @[ (__bridge id)[UIColor blueColor].CGColor, (__bridge id)[UIColor blackColor].CGColor ];
    [self.view addSubview:self.gradientView];
}

@end

結果は次のとおりです。

良い回転

于 2013-07-09T21:38:01.723 に答える
3

このコードを挿入してwillAnimateRotationToInterfaceOrientation:duration:実装を削除すると、見栄えが良くなります。

- (void)viewWillLayoutSubviews
{
    [[[self.view.layer sublayers] objectAtIndex:0] setFrame:self.view.bounds];    
}

ただし、これはあまりエレガントではありません。実際のアプリケーションでは、UIView をサブクラス化してグラデーション ビューを作成する必要があります。このカスタム ビューでは、layerClass をオーバーライドして、グラデーション レイヤーによってサポートされるようにすることができます。

+ (Class)layerClass
{
  return [CAGradientLayer class];
}

またlayoutSubviews、ビューの境界が変更されたときに処理するように実装します。

この背景ビューを作成するときは、インターフェイスの回転時に境界が自動的に調整されるように、自動サイズ変更マスクを使用します。

于 2013-07-09T19:37:26.073 に答える
1

別の迅速なバージョン - drawRect を使用していません。

class UIGradientView: UIView {
    override class func layerClass() -> AnyClass {
        return CAGradientLayer.self
    }

    var gradientLayer: CAGradientLayer {
        return layer as! CAGradientLayer
    }

    func setGradientBackground(colors: [UIColor], startPoint: CGPoint = CGPoint(x: 0.5, y: 0), endPoint: CGPoint = CGPoint(x: 0.5, y: 1)) {
        gradientLayer.startPoint = startPoint
        gradientLayer.endPoint = endPoint
        gradientLayer.colors = colors.map({ (color) -> CGColor in return color.CGColor })
    }
}

コントローラーでは、次のように呼び出します。

gradientView.setGradientBackground([UIColor.grayColor(), UIColor.whiteColor()])
于 2016-04-10T18:15:51.753 に答える
0

個人的には、ビューのサブクラス内ですべてを自己完結型にしておくことを好みます。

これが私のSwift実装です:

            import UIKit

            @IBDesignable
            class GradientBackdropView: UIView {

                @IBInspectable var startColor: UIColor=UIColor.whiteColor()
                @IBInspectable var endColor: UIColor=UIColor.whiteColor()
                @IBInspectable var intermediateColor: UIColor=UIColor.whiteColor()

                var gradientLayer: CAGradientLayer?

                // Only override drawRect: if you perform custom drawing.
                // An empty implementation adversely affects performance during animation.
                override func drawRect(rect: CGRect) {
                    // Drawing code
                    super.drawRect(rect)

                    if gradientLayer == nil {
                        self.addGradientLayer(rect: rect)
                    } else {
                        gradientLayer?.removeFromSuperlayer()
                        gradientLayer=nil
                        self.addGradientLayer(rect: rect)
                    }
                }


                override func layoutSubviews() {
                    super.layoutSubviews()

                    if gradientLayer == nil {
                        self.addGradientLayer(rect: self.bounds)
                    } else {
                        gradientLayer?.removeFromSuperlayer()
                        gradientLayer=nil
                        self.addGradientLayer(rect: self.bounds)
                    }
                }


                func addGradientLayer(rect rect:CGRect) {
                    gradientLayer=CAGradientLayer()

                    gradientLayer?.frame=self.bounds

                    gradientLayer?.colors=[startColor.CGColor,intermediateColor.CGColor,endColor.CGColor]

                    gradientLayer?.startPoint=CGPointMake(0.0, 1.0)
                    gradientLayer?.endPoint=CGPointMake(0.0, 0.0)

                    gradientLayer?.locations=[NSNumber(float: 0.1),NSNumber(float: 0.5),NSNumber(float: 1.0)]

                    self.layer.insertSublayer(gradientLayer!, atIndex: 0)

                    gradientLayer?.transform=self.layer.transform
                }
            }
于 2016-03-19T07:28:10.567 に答える
0

簡単な方法。ビューのサイズが変わるたびに、グラデーション レイヤーを追加できます。

class YourVC: UIViewController {
...
override func viewDidLoad() {
    super.viewDidLoad()
    yourView.addObserver(self, forKeyPath: "bounds", options: [], context: nil)
}
...
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if (object as? NSObject == yourView && keyPath == "bounds") {
        //remove and add again your gradient layer
    }
}
...
于 2021-12-09T09:41:01.300 に答える
0

これは、ストーリーボード、xib、またはコードから使用できます。後で色を動的に変更できます(私の場合はそれが必要でした)

ここに完全なコピーペースト可能なものを追加します:

import UIKit

class GradientView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupView()
    }

    private func setupView() {
        autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }

    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }
}

extension GradientView {
    func setVerticalGradientBackground(colors: [CGColor], locations: [CGFloat] = [0, 1]) {
        setGradientBackground(colors: colors, locations: locations, startPoint: .init(x: 0.5, y: 0), endPoint: .init(x: 0.5, y: 1))
    }

    func setHorizontalGradientBackground(colors: [CGColor], locations: [CGFloat] = [0, 1]) {
        setGradientBackground(colors: colors, locations: locations, startPoint: .init(x: 0, y: 0.5), endPoint: .init(x: 1, y: 0.5))
    }

    func setGradientBackground(colors: [CGColor],
                               locations: [CGFloat],
                               startPoint: CGPoint,
                               endPoint: CGPoint) {

        guard let gradientLayer = self.layer as? CAGradientLayer else {
            return
        }

        gradientLayer.colors = colors
        gradientLayer.locations = locations.map { $0 as NSNumber }
        gradientLayer.startPoint = startPoint
        gradientLayer.endPoint = endPoint
        gradientLayer.frame = bounds
    }
}
于 2021-02-04T09:37:04.643 に答える