다음 CALayer가 있습니다.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(8, 57, 296, 30);
gradient.cornerRadius = 3.0f;
gradient.colors = [NSArray arrayWithObjects:(id)[RGB(130, 0, 140) CGColor], (id)[RGB(108, 0, 120) CGColor], nil];
[self.layer insertSublayer:gradient atIndex:0];
내부 그림자 효과 를 추가하고 싶지만 어떻게해야할지 잘 모르겠습니다. drawRect에서 그릴 필요가 있다고 생각하지만 일부 버튼 뒤에 막대가 있어야하므로 다른 UIView 객체 위에 레이어를 추가하므로 어떻게 해야할지 모르겠습니다.
다른 레이어를 추가 할 수 있지만 내부 그림자 효과를 얻는 방법을 다시 알 수 없습니다 (예 :
감사합니다 …
답변
Costique 제안에 따라 Core Graphics를 사용하여 내부 그림자를 그리는 방법을 궁금해하는 사람은 다음과 같습니다. (iOS에서 필요에 따라 조정)
drawRect : 메서드에서 …
CGRect bounds = [self bounds];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = 0.5f * CGRectGetHeight(bounds);
// Create the "visible" path, which will be the shape that gets the inner shadow
// In this case it's just a rounded rect, but could be as complex as your want
CGMutablePathRef visiblePath = CGPathCreateMutable();
CGRect innerRect = CGRectInset(bounds, radius, radius);
CGPathMoveToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x + innerRect.size.width, bounds.origin.y);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y, bounds.origin.x + bounds.size.width, innerRect.origin.y, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, innerRect.origin.y + innerRect.size.height);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, innerRect.origin.x + innerRect.size.width, bounds.origin.y + bounds.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y + bounds.size.height);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x, bounds.origin.y + bounds.size.height, bounds.origin.x, innerRect.origin.y + innerRect.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x, innerRect.origin.y);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x, bounds.origin.y, innerRect.origin.x, bounds.origin.y, radius);
CGPathCloseSubpath(visiblePath);
// Fill this path
UIColor *aColor = [UIColor redColor];
[aColor setFill];
CGContextAddPath(context, visiblePath);
CGContextFillPath(context);
// Now create a larger rectangle, which we're going to subtract the visible path from
// and apply a shadow
CGMutablePathRef path = CGPathCreateMutable();
//(when drawing the shadow for a path whichs bounding box is not known pass "CGPathGetPathBoundingBox(visiblePath)" instead of "bounds" in the following line:)
//-42 cuould just be any offset > 0
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));
// Add the visible path (so that it gets subtracted for the shadow)
CGPathAddPath(path, NULL, visiblePath);
CGPathCloseSubpath(path);
// Add the visible paths as the clipping path to the context
CGContextAddPath(context, visiblePath);
CGContextClip(context);
// Now setup the shadow properties on the context
aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 3.0f, [aColor CGColor]);
// Now fill the rectangle, so the shadow gets drawn
[aColor setFill];
CGContextSaveGState(context);
CGContextAddPath(context, path);
CGContextEOFillPath(context);
// Release the paths
CGPathRelease(path);
CGPathRelease(visiblePath);
따라서 기본적으로 다음 단계가 있습니다.
- 경로 만들기
- 원하는 채우기 색상을 설정하고이 경로를 컨텍스트에 추가하고 컨텍스트를 채 웁니다.
- 이제 보이는 경로를 묶을 수있는 더 큰 직사각형을 만듭니다. 이 경로를 닫기 전에 보이는 경로를 추가하십시오. 그런 다음 경로를 닫아서 보이는 경로를 뺀 모양을 만듭니다. 이러한 경로를 만든 방법에 따라 채우기 방법 (짝수 / 홀수가 아닌 0이 아닌 감기)을 조사 할 수 있습니다. 본질적으로 하위 경로를 함께 더할 때 “빼기”를 얻으려면 시계 방향과 시계 반대 방향의 반대 방향으로 하위 경로를 그리거나 구성해야합니다.
- 그런 다음 보이는 경로를 컨텍스트의 클리핑 경로로 설정해야 화면 외부에 아무것도 그리지 않습니다.
- 그런 다음 오프셋, 흐림 및 색상을 포함하는 컨텍스트에 그림자를 설정합니다.
- 그런 다음 큰 모양에 구멍을 채우십시오. 색상은 중요하지 않습니다. 모든 것을 올바르게 수행했다면이 색상이 아닌 그림자 만 볼 수 있기 때문입니다.
답변
이 파티에 늦었다는 건 알지만, 여행을 일찍하는데 도움이되었을 것입니다 …
신용이 필요한 곳에 신용을 부여하기 위해 이것은 본질적으로 더 큰 지역에서 더 작은 지역을 빼는 Costique의 솔루션에 대한 Daniel Thorpe의 정교화를 수정 한 것입니다. 이 버전은 재정의하는 대신 레이어 구성을 사용하는 사용자를위한 것입니다.-drawRect:
CAShapeLayer
클래스는 동일한 효과를 달성 할 수 있습니다 :
CAShapeLayer* shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:[self bounds]];
// Standard shadow stuff
[shadowLayer setShadowColor:[[UIColor colorWithWhite:0 alpha:1] CGColor]];
[shadowLayer setShadowOffset:CGSizeMake(0.0f, 0.0f)];
[shadowLayer setShadowOpacity:1.0f];
[shadowLayer setShadowRadius:5];
// Causes the inner region in this example to NOT be filled.
[shadowLayer setFillRule:kCAFillRuleEvenOdd];
// Create the larger rectangle path.
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));
// Add the inner path so it's subtracted from the outer path.
// someInnerPath could be a simple bounds rect, or maybe
// a rounded one for some extra fanciness.
CGPathAddPath(path, NULL, someInnerPath);
CGPathCloseSubpath(path);
[shadowLayer setPath:path];
CGPathRelease(path);
[[self layer] addSublayer:shadowLayer];
이 시점에서 부모 레이어가 경계까지 마스킹하지 않으면 레이어 가장자리 주변에 마스크 레이어의 추가 영역이 표시됩니다. 예제를 직접 복사 한 경우 42 픽셀의 검은 색이됩니다. 이를 제거하려면 CAShapeLayer
동일한 경로를 가진 다른 것을 사용 하고 그림자 레이어의 마스크로 설정하면됩니다.
CAShapeLayer* maskLayer = [CAShapeLayer layer];
[maskLayer setPath:someInnerPath];
[shadowLayer setMask:maskLayer];
나는 이것을 직접 벤치마킹하지 않았지만 래스터 화와 함께이 접근 방식을 사용하는 것이을 재정의하는 것보다 더 성능이 좋다고 생각 -drawRect:
합니다.
답변
경계 외부에 큰 직사각형 경로를 만들고 경계 크기의 직사각형 경로를 빼고 결과 경로를 “일반”그림자로 채움으로써 Core Graphics로 내부 그림자를 그릴 수 있습니다.
그러나 그래디언트 레이어와 결합해야하므로 더 쉬운 해결책은 내부 그림자의 9 부분 투명 PNG 이미지를 만들어 올바른 크기로 늘리는 것입니다. 9 부분으로 구성된 그림자 이미지는 다음과 같습니다 (크기는 21×21 픽셀).
CALayer *innerShadowLayer = [CALayer layer];
innerShadowLayer.contents = (id)[UIImage imageNamed: @"innershadow.png"].CGImage;
innerShadowLayer.contentsCenter = CGRectMake(10.0f/21.0f, 10.0f/21.0f, 1.0f/21.0f, 1.0f/21.0f);
그런 다음 innerShadowLayer의 프레임을 설정하면 그림자가 제대로 늘어납니다.
답변
Swift에서 CALayer 만 사용하는 단순화 된 버전 :
import UIKit
final class FrameView : UIView {
init() {
super.init(frame: CGRect.zero)
backgroundColor = UIColor.white
}
@available(*, unavailable)
required init?(coder decoder: NSCoder) { fatalError("unavailable") }
override func layoutSubviews() {
super.layoutSubviews()
addInnerShadow()
}
private func addInnerShadow() {
let innerShadow = CALayer()
innerShadow.frame = bounds
// Shadow path (1pt ring around bounds)
let path = UIBezierPath(rect: innerShadow.bounds.insetBy(dx: -1, dy: -1))
let cutout = UIBezierPath(rect: innerShadow.bounds).reversing()
path.append(cutout)
innerShadow.shadowPath = path.cgPath
innerShadow.masksToBounds = true
// Shadow properties
innerShadow.shadowColor = UIColor(white: 0, alpha: 1).cgColor // UIColor(red: 0.71, green: 0.77, blue: 0.81, alpha: 1.0).cgColor
innerShadow.shadowOffset = CGSize.zero
innerShadow.shadowOpacity = 1
innerShadow.shadowRadius = 3
// Add
layer.addSublayer(innerShadow)
}
}
innerShadow 레이어는 그림자 앞에 렌더링되므로 불투명 한 배경색이 없어야합니다.
답변
약간의 순환 방식이지만 이미지를 사용할 필요가 없으며 (읽기 : 색상 변경, 그림자 반경 등) 코드 몇 줄이면 충분합니다.
-
드롭 섀도우를 적용 할 UIView의 첫 번째 하위보기로 UIImageView를 추가합니다. IB를 사용하지만 프로그래밍 방식으로 동일한 작업을 수행 할 수 있습니다.
-
UIImageView에 대한 참조가 ‘innerShadow’라고 가정합니다.
`
[[innerShadow layer] setMasksToBounds:YES];
[[innerShadow layer] setCornerRadius:12.0f];
[[innerShadow layer] setBorderColor:[UIColorFromRGB(180, 180, 180) CGColor]];
[[innerShadow layer] setBorderWidth:1.0f];
[[innerShadow layer] setShadowColor:[UIColorFromRGB(0, 0, 0) CGColor]];
[[innerShadow layer] setShadowOffset:CGSizeMake(0, 0)];
[[innerShadow layer] setShadowOpacity:1];
[[innerShadow layer] setShadowRadius:2.0];
주의 사항 : 테두리가 있어야합니다. 그렇지 않으면 그림자가 표시되지 않습니다. [UIColor clearColor]가 작동하지 않습니다. 이 예에서는 다른 색을 사용하지만 그림자의 시작과 같은 색을 갖도록 엉망으로 만들 수 있습니다. 🙂
UIColorFromRGB
매크로 에 대한 아래 bbrame의 설명을 참조하십시오 .
답변
안하는 것보다 늦게하는 것이 낫다…
여기에 이미 게시 된 것보다 낫지는 않은 또 다른 접근 방식이 있지만 멋지고 간단합니다.
-(void)drawInnerShadowOnView:(UIView *)view
{
UIImageView *innerShadowView = [[UIImageView alloc] initWithFrame:view.bounds];
innerShadowView.contentMode = UIViewContentModeScaleToFill;
innerShadowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[view addSubview:innerShadowView];
[innerShadowView.layer setMasksToBounds:YES];
[innerShadowView.layer setBorderColor:[UIColor lightGrayColor].CGColor];
[innerShadowView.layer setShadowColor:[UIColor blackColor].CGColor];
[innerShadowView.layer setBorderWidth:1.0f];
[innerShadowView.layer setShadowOffset:CGSizeMake(0, 0)];
[innerShadowView.layer setShadowOpacity:1.0];
// this is the inner shadow thickness
[innerShadowView.layer setShadowRadius:1.5];
}
답변
drawRect로 내부 그림자를 그리거나 UIView를 View에 추가하는 대신. 예를 들어 UIView V의 바닥에 내부 그림자 효과를 원하는 경우 CALayer를 테두리에 직접 추가 할 수 있습니다.
innerShadowOwnerLayer = [[CALayer alloc]init];
innerShadowOwnerLayer.frame = CGRectMake(0, V.frame.size.height+2, V.frame.size.width, 2);
innerShadowOwnerLayer.backgroundColor = [UIColor whiteColor].CGColor;
innerShadowOwnerLayer.shadowColor = [UIColor blackColor].CGColor;
innerShadowOwnerLayer.shadowOffset = CGSizeMake(0, 0);
innerShadowOwnerLayer.shadowRadius = 10.0;
innerShadowOwnerLayer.shadowOpacity = 0.7;
[V.layer addSubLayer:innerShadowOwnerLayer];
이것은 대상 UIView에 대한 하단 내부 그림자를 추가합니다.