Как я могу добавить градиент, который охватывает два вида?

Я знаю, как это сделать (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 ответа

Решение

Есть несколько способов сделать это. Вот один из способов:

  1. Создать UIView подкласс имени GradientView управлять градиентным слоем. Это полезно, потому что это означает, что вы можете использовать обычные методы UIKit для управления макетом градиента (ограничения автоматического макета, маски автоматического изменения размера, анимации UIKit).

  2. Для каждого представления, которое должно участвовать в общем градиенте, добавьте GradientView подтаблицы. Настройте каждый GradientViewЦвета, местоположения, начальная и конечная точки одинаковы.

  3. Для каждого вида, который должен участвовать в общем градиенте, включите clipsToBounds,

  4. Используйте автоматические ограничения макета, чтобы сделать каждый 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
Другие вопросы по тегам