Как я могу добавить градиент, который охватывает два вида?
Я знаю, как это сделать (1), но как я могу сделать (2)?
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = @[(id)[UIColor blueColor].CGColor, (id)[UIColor redColor].CGColor];
[view.layer insertSublayer:gradient atIndex:0];
2 ответа
Есть несколько способов сделать это. Вот один из способов:
Создать
UIView
подкласс имениGradientView
управлять градиентным слоем. Это полезно, потому что это означает, что вы можете использовать обычные методы UIKit для управления макетом градиента (ограничения автоматического макета, маски автоматического изменения размера, анимации UIKit).Для каждого представления, которое должно участвовать в общем градиенте, добавьте
GradientView
подтаблицы. Настройте каждыйGradientView
Цвета, местоположения, начальная и конечная точки одинаковы.Для каждого вида, который должен участвовать в общем градиенте, включите
clipsToBounds
,Используйте автоматические ограничения макета, чтобы сделать каждый
GradientView
охватить все участвующие супервизоры. (Важно понимать, что ограничения могут пересекать границы суперпредставления / подпредставления).
При таком подходе автоматическая разметка заботится о том, чтобы градиент покрывал все виды, даже если они меняют размер или перемещаются. Например, вам не нужно будет делать ничего особенного, чтобы красиво анимировать градиенты, когда пользователь поворачивает устройство.
Таким образом, для вашего примера с двумя представлениями я предлагаю вам настроить иерархию представлений следующим образом:
На приведенном выше скриншоте отладчика я отключил отсечение. Вы можете видеть, что два градиентных вида имеют одинаковые градиенты и имеют одинаковое пространство экрана. topGradient
это подвид topView
а также bottomGradient
это подвид bottomView
,
Если мы включим отсечение, вы увидите только часть topGradient
это вписывается внутрь topView
границы, и вы увидите только часть bottomGradient
это вписывается внутрь bottomView
границы. Вот как это выглядит с включенным отсечением:
А вот скриншот моей тестовой программы в симуляторе:
Вот исходный код GradientView
:
@interface GradientView: UIView
@property (nonatomic, strong, readonly) CAGradientLayer *gradientLayer;
@end
@implementation GradientView
+ (Class)layerClass { return CAGradientLayer.class; }
- (CAGradientLayer *)gradientLayer { return (CAGradientLayer *)self.layer; }
@end
Вот код, который я использовал для создания всех представлений:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *topView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 50)];
topView.layer.cornerRadius = 10;
topView.clipsToBounds = YES;
UIView *topGradient = [self newGradientView];
[topView addSubview:topGradient];
[self.view addSubview:topView];
UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(20, 90, 100, 50)];
bottomView.layer.cornerRadius = 10;
bottomView.clipsToBounds = YES;
UIView *bottomGradient = [self newGradientView];
[bottomView addSubview:bottomGradient];
[self.view addSubview:bottomView];
[self constrainView:topGradient toCoverViews:@[topView, bottomView]];
[self constrainView:bottomGradient toCoverViews:@[topView, bottomView]];
}
- (GradientView *)newGradientView {
GradientView *gv = [[GradientView alloc] initWithFrame:CGRectZero];
gv.translatesAutoresizingMaskIntoConstraints = NO;
gv.gradientLayer.colors = @[(__bridge id)UIColor.blueColor.CGColor, (__bridge id)UIColor.redColor.CGColor];
return gv;
}
И вот как я создаю ограничения, которые делают GradientView
(или любой вид) охватывает набор видов:
- (void)constrainView:(UIView *)coverer toCoverViews:(NSArray<UIView *> *)coverees {
for (UIView *coveree in coverees) {
NSArray<NSLayoutConstraint *> *cs;
cs = @[
[coverer.leftAnchor constraintLessThanOrEqualToAnchor:coveree.leftAnchor],
[coverer.rightAnchor constraintGreaterThanOrEqualToAnchor:coveree.rightAnchor],
[coverer.topAnchor constraintLessThanOrEqualToAnchor:coveree.topAnchor],
[coverer.bottomAnchor constraintGreaterThanOrEqualToAnchor:coveree.bottomAnchor]];
[NSLayoutConstraint activateConstraints:cs];
cs = @[
[coverer.leftAnchor constraintEqualToAnchor:coveree.leftAnchor],
[coverer.rightAnchor constraintEqualToAnchor:coveree.rightAnchor],
[coverer.topAnchor constraintEqualToAnchor:coveree.topAnchor],
[coverer.bottomAnchor constraintEqualToAnchor:coveree.bottomAnchor]];
for (NSLayoutConstraint *c in cs) { c.priority = UILayoutPriorityDefaultHigh; }
[NSLayoutConstraint activateConstraints:cs];
}
}
greaterThanOrEqual
/lessThanOrEqual
ограничения, которые (по умолчанию) имеют требуемый приоритет, гарантируют, что coverer
охватывает весь кадр каждого coveree
, equal
ограничения, которые имеют более низкий приоритет, затем убедитесь, что coverer
занимает минимальное пространство, необходимое для покрытия каждого coveree
,
Вы можете сделать это, добавив вид поверх градиента, а затем вырезать фигуры, сделав маску из UIBezierPath
, затем добавив это к представлению сверху (давайте назовем это topView
):
let yourPath: UIBezierPath = //create the desired bezier path for your shapes
let mask = CAShapeLayer()
mask.path = yourPath.cgPath
topView.layer.mask = mask