UIViewの下に影を描くにはどうすればよいですか?
-
03-07-2019 - |
質問
Cocoa Touchの UIView
の下端に影を描画しようとしています。 CGContextSetShadow()
を使用して影を描画する必要があることを理解していますが、Quartz 2Dプログラミングガイドは少し曖昧です:
- グラフィックスの状態を保存します。
- 適切な値を渡し、関数
CGContextSetShadow
を呼び出します。 - シャドウを適用するすべての図面を実行します。
- グラフィックスの状態を復元する
UIView
サブクラスで次のことを試しました:
- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
CGContextRestoreGState(currentContext);
[super drawRect: rect];
}
..しかし、これは私にとってはうまくいかず、(a)次に進むべき場所と(b) UIView
に対して何かする必要がある場合これを機能させますか?
解決
現在のコードでは、現在のコンテキストの GState
を保存し、シャドウを描画するように設定します。シャドウを描画するように設定する前の状態に復元します。そして最後に、スーパークラスの drawRect
の実装を呼び出します:。
シャドウ設定の影響を受ける描画は、後
に発生する必要がありますCGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
しかし前
CGContextRestoreGState(currentContext);
では、スーパークラスの drawRect:
をシャドウに「ラップ」したい場合、このようにコードを再配置したらどうですか?
- (void)drawRect:(CGRect)rect {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(currentContext);
CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
[super drawRect: rect];
CGContextRestoreGState(currentContext);
}
他のヒント
はるかに簡単なアプローチは、初期化時にビューのいくつかのレイヤー属性を設定することです:
self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;
QuartzCoreをインポートする必要があります。
#import <QuartzCore/QuartzCore.h>
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 8; // if you like rounded corners
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;
これにより、アプリケーションの速度が低下します。 次の行を追加すると、表示が長方形である限りパフォーマンスが向上します。
self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
同じソリューションですが、念のため:ストーリーボードで直接シャドウを定義できます。
例:
これを試すことができます....値をいじることができます。
shadowRadius
はぼかしの量を決定します。 shadowOffset
は、影の行き先を指示します。
Swift 2.0
let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height
demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4) //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds = false
demoView.layer.shadowPath = shadowPath.CGPath
Swift 3.0
let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height
demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.black.cgColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4) //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds = false
demoView.layer.shadowPath = shadowPath.cgPath
スプレッドの例
基本的なシャドウを作成するには
demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSizeMake(0.5, 4.0); //Here your control your spread
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
Swift 2.0の基本的なシャドウの例
Interface Builderを使用したシンプルでクリーンなソリューション
UIView.swiftという名前のファイルをプロジェクトに追加します(または、これを任意のファイルに貼り付けます):
import UIKit
@IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
* from patterns are currently NOT supported. Animatable. */
@IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.CGColor
}
get {
if let color = layer.shadowColor {
return UIColor(CGColor:color)
}
else {
return nil
}
}
}
/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
* [0,1] range will give undefined results. Animatable. */
@IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
/* The shadow offset. Defaults to (0, -3). Animatable. */
@IBInspectable var shadowOffset: CGPoint {
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
}
/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
@IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
}
これは、ユーティリティパネルのすべてのビューのInterface Builderで使用できます&gt;属性インスペクター:
シャドウを簡単に設定できるようになりました。
注:
-影はIBには表示されず、実行時にのみ表示されます。
-マゼン・カッサーが言ったように
これを機能させることに失敗した人に[...]クリップサブビュー(
clipsToBounds
)が有効になっていないことを確認してください
これをユーティリティの一部として使用します。これにより、シャドウを設定できるだけでなく、 UIView
の角を丸くすることもできます。また、好きな色の影を設定できます。通常は黒が好まれますが、背景が白でない場合は、別のものが必要な場合があります。使用するものは次のとおりです。
in utils.m
+ (void)roundedLayer:(CALayer *)viewLayer
radius:(float)r
shadow:(BOOL)s
{
[viewLayer setMasksToBounds:YES];
[viewLayer setCornerRadius:r];
[viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
[viewLayer setBorderWidth:1.0f];
if(s)
{
[viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
[viewLayer setShadowOffset:CGSizeMake(0, 0)];
[viewLayer setShadowOpacity:1];
[viewLayer setShadowRadius:2.0];
}
return;
}
これを使用するには、これを呼び出す必要があります- [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];
Swift 3
extension UIView {
func installShadow() {
layer.cornerRadius = 2
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1)
layer.shadowOpacity = 0.45
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shadowRadius = 1.0
}
}
StoryBoardを使用し、ランタイム属性の入力を続けたくない場合は、ビューの拡張機能を簡単に作成し、ストーリーボードで使用できるようにすることができます。
ステップ1.拡張機能を作成する
extension UIView {
@IBInspectable var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}
@IBInspectable var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
@IBInspectable var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
@IBInspectable var maskToBound: Bool {
get {
return layer.masksToBounds
}
set {
layer.masksToBounds = newValue
}
}
}
ここですべての答えを試した後、これを機能させることに失敗した人(自分として!)に、属性インスペクターで Clip Subviews が有効になっていないことを確認してください...
Swift 3
self.paddingView.layer.masksToBounds = false
self.paddingView.layer.shadowOffset = CGSize(width: -15, height: 10)
self.paddingView.layer.shadowRadius = 5
self.paddingView.layer.shadowOpacity = 0.5
以下のように、シャドウとコーナーの半径用に作成されたユーティリティ関数を使用できます。
- (void)addShadowWithRadius:(CGFloat)shadowRadius withShadowOpacity:(CGFloat)shadowOpacity withShadowOffset:(CGSize)shadowOffset withShadowColor:(UIColor *)shadowColor withCornerRadius:(CGFloat)cornerRadius withBorderColor:(UIColor *)borderColor withBorderWidth:(CGFloat)borderWidth forView:(UIView *)view{
// drop shadow
[view.layer setShadowRadius:shadowRadius];
[view.layer setShadowOpacity:shadowOpacity];
[view.layer setShadowOffset:shadowOffset];
[view.layer setShadowColor:shadowColor.CGColor];
// border radius
[view.layer setCornerRadius:cornerRadius];
// border
[view.layer setBorderColor:borderColor.CGColor];
[view.layer setBorderWidth:borderWidth];
}
お役に立てば幸いです!!!
すべての回答に問題はありませんが、もう1点追加したい
テーブルセルがあるときに問題が発生した場合、新しいセルをDequeしてシャドウに不一致があるため、この場合、すべての条件で適切に動作するように、layoutSubviewsメソッドにシャドウコードを配置する必要があります。
-(void)layoutSubviews{
[super layoutSubviews];
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
[VPShadow applyShadowView:self];
}
または特定のビューのViewControllersでは、次のメソッド内にシャドウコードを配置して、うまく機能するようにします
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self.viewShadow layoutIfNeeded];
[VPShadow applyShadowView:self.viewShadow];
}
より一般化された形式のために、新しい開発者向けにシャドウ実装を変更しましたex:
/*!
@brief Add shadow to a view.
@param layer CALayer of the view.
*/
+(void)applyShadowOnView:(CALayer *)layer OffsetX:(CGFloat)x OffsetY:(CGFloat)y blur:(CGFloat)radius opacity:(CGFloat)alpha RoundingCorners:(CGFloat)cornerRadius{
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:cornerRadius];
layer.masksToBounds = NO;
layer.shadowColor = [UIColor blackColor].CGColor;
layer.shadowOffset = CGSizeMake(x,y);// shadow x and y
layer.shadowOpacity = alpha;
layer.shadowRadius = radius;// blur effect
layer.shadowPath = shadowPath.CGPath;
}
仲間のXamariansの場合、Xamarin.iOS / C#バージョンの回答は次のようになります。
public override void DrawRect(CGRect area, UIViewPrintFormatter formatter)
{
CGContext currentContext = UIGraphics.GetCurrentContext();
currentContext.SaveState();
currentContext.SetShadow(new CGSize(-15, 20), 5);
base.DrawRect(area, formatter);
currentContext.RestoreState();
}
主な違いは、適切なメソッドを直接呼び出す CGContext
のインスタンスを取得することです。