Как создать эффект верхнего затухания с помощью UIScrollView?

У меня есть UIScrollView в моем приложении, и я видел в некоторых других приложениях, которые, когда пользователь прокручивается, верхняя часть исчезает на свитке, а не просто исчезает.

Мне очень нравится этот эффект и я хочу его достичь. Любые идеи, как это можно сделать?

Ответ 1

Просто основанный на большом ответе Роб, здесь категория на UIView,

@implementation UIView (Looks)

-(void)fadeTail
    {
    CAGradientLayer *gradient = [CAGradientLayer layer];
    gradient.frame = self.bounds;
    gradient.colors = @[
        (id)[[UIColor whiteColor] CGColor],
        (id)[[UIColor clearColor] CGColor]
        ];
    gradient.startPoint = CGPointMake(0.5, 0.93);
    gradient.endPoint = CGPointMake(0.5, 1);

    [self.layer setMask:gradient];
    }

@end

который делает это.. рассматривается здесь на небольшом UIWebView...

enter image description here

(Чтобы быть ясным, сам веб-представление белый, веб-представление, похоже, сидит на фоне серого цвета. Таким образом, функция fadeTail в основном угасает "весь" веб-просмотр, что бы ни случилось внизу.)

Надеюсь, что это поможет кому-то немного набирать текст, приветствия

Ответ 2

РЕДАКТИРОВАТЬ: Я поставил этот код на github, см. здесь.


Посмотрите мой ответ на аналогичный вопрос.

Мое решение относится к подклассу UIScrollView и создает слой маски в методе layoutSubviews.

#import "FadingScrollView.h"
#import <QuartzCore/QuartzCore.h>

static float const fadePercentage = 0.2;

@implementation FadingScrollView

// ...

- (void)layoutSubviews
{
    [super layoutSubviews];

    NSObject * transparent = (NSObject *) [[UIColor colorWithWhite:0 alpha:0] CGColor];
    NSObject * opaque = (NSObject *) [[UIColor colorWithWhite:0 alpha:1] CGColor];

    CALayer * maskLayer = [CALayer layer];
    maskLayer.frame = self.bounds;

    CAGradientLayer * gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = CGRectMake(self.bounds.origin.x, 0,
                                     self.bounds.size.width, self.bounds.size.height);

    gradientLayer.colors = [NSArray arrayWithObjects: transparent, opaque,
                            opaque, transparent, nil];

    // Set percentage of scrollview that fades at top & bottom
    gradientLayer.locations = [NSArray arrayWithObjects:
                               [NSNumber numberWithFloat:0],
                               [NSNumber numberWithFloat:fadePercentage],
                               [NSNumber numberWithFloat:1.0 - fadePercentage],
                               [NSNumber numberWithFloat:1], nil];

    [maskLayer addSublayer:gradientLayer];
    self.layer.mask = maskLayer;
}

@end

Приведенный выше код стирает верхнюю и нижнюю стороны UIScrollView от цвета фона до прозрачного, но это можно легко изменить, чтобы вытереть только верх (или выцветать до любого цвета, который вы хотите).

Измените эту строку, чтобы вытереть только верх:

// Fade top of scrollview only
gradientLayer.locations = [NSArray arrayWithObjects:
                           [NSNumber numberWithFloat:0],
                           [NSNumber numberWithFloat:fadePercentage],
                           [NSNumber numberWithFloat:1],
                           [NSNumber numberWithFloat:1], nil];

ИЗМЕНИТЬ 2:

Или затухайте сверху только путем изменения этих двух строк:

// Fade top of scrollview only
    gradientLayer.colors = [NSArray arrayWithObjects: transparent, opaque, nil];

    gradientLayer.locations = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0],
                                                         [NSNumber numberWithFloat:fadePercentage], nil];

Или, затухайте только снизу:

// Fade bottom of scrollview only
    gradientLayer.colors = [NSArray arrayWithObjects: opaque, transparent, nil];

    gradientLayer.locations = [NSArray arrayWithObjects: [NSNumber numberWithFloat:1.0 - fadePercentage],
                                                         [NSNumber numberWithFloat:1], nil];

Ответ 3

Вы можете использовать CAGradientLayer на

  • Добавление в проект проекта QuartzCore.framework(см. Связь с библиотекой или Framework).

  • Добавьте #import заголовков QuartzCore:

    #import <QuartzCore/QuartzCore.h>
    
  • И затем используйте CAGradientLayer:

    - (void)addGradientMaskToView:(UIView *)view
    {
        CAGradientLayer *gradient = [CAGradientLayer layer];
        gradient.frame = view.bounds;
        gradient.colors = @[(id)[[UIColor clearColor] CGColor], (id)[[UIColor whiteColor] CGColor]];
        gradient.startPoint = CGPointMake(0.5, 0.0); // this is the default value, so this line is not needed
        gradient.endPoint = CGPointMake(0.5, 0.20);
    
        [view.layer setMask:gradient];
    }
    

Обратите внимание: этот CAGradientLayer является градиентом от цвета с альфой 0.0 (например, clearColor) до цвета к цвету с альфой 1.0 (например, whiteColor), а не только от черного до белого. Вы можете настроить startPoint (значение по умолчанию, вероятно, хорошо) и endPoint, чтобы настроить, где вы хотите применить градиент.

И вообще, когда вы делаете это с помощью UIScrollView, если вы не хотите, чтобы градиент прокручивался вместе с вами, вы делаете UIScrollView подвью другого UIView и применяете этот градиент к этому представлению контейнера, а не к просмотр прокрутки.

Ответ 4

Благодаря ответу Fattie я создал следующее расширение UIView в Swift, которое заботится о затухании градиента и предоставляет больше стилей (снизу, сверху, слева направо, по вертикали и горизонтали), а также процент затухания.

Если у вас есть какие-либо комментарии или рекомендации, сообщите мне об этом в сущности, которую я создал. Я стараюсь обновлять и этот, и этот ответ с учетом всех внесенных изменений.

Расширение:

extension UIView {

    enum UIViewFadeStyle {
        case bottom
        case top
        case left
        case right

        case vertical
        case horizontal
    }

    func fadeView(style: UIViewFadeStyle = .bottom, percentage: Double = 0.07) {
        let gradient = CAGradientLayer()
        gradient.frame = bounds
        gradient.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]

        let startLocation = percentage
        let endLocation = 1 - percentage

        switch style {
        case .bottom:
            gradient.startPoint = CGPoint(x: 0.5, y: endLocation)
            gradient.endPoint = CGPoint(x: 0.5, y: 1)
        case .top:
            gradient.startPoint = CGPoint(x: 0.5, y: startLocation)
            gradient.endPoint = CGPoint(x: 0.5, y: 0.0)
        case .vertical:
            gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
            gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
            gradient.colors = [UIColor.clear.cgColor, UIColor.white.cgColor, UIColor.white.cgColor, UIColor.clear.cgColor]
            gradient.locations = [0.0, startLocation, endLocation, 1.0] as [NSNumber]

        case .left:
            gradient.startPoint = CGPoint(x: startLocation, y: 0.5)
            gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
        case .right:
            gradient.startPoint = CGPoint(x: endLocation, y: 0.5)
            gradient.endPoint = CGPoint(x: 1, y: 0.5)
        case .horizontal:
            gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
            gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
            gradient.colors = [UIColor.clear.cgColor, UIColor.white.cgColor, UIColor.white.cgColor, UIColor.clear.cgColor]
            gradient.locations = [0.0, startLocation, endLocation, 1.0] as [NSNumber]
        }

        layer.mask = gradient
    }

}

Ответ 5

Вы добавляете альфа-маску к виду, содержащему ваше прокрутку, например:

CALayer *mask = [CALayer layer];
CGImageRef maskRef = [UIImage imageNamed:@"scrollMask"].CGImage;
CGImageRef maskImage = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                             CGImageGetHeight(maskRef),
                                             CGImageGetBitsPerComponent(maskRef),
                                             CGImageGetBitsPerPixel(maskRef),
                                             CGImageGetBytesPerRow(maskRef),
                                             CGImageGetDataProvider(maskRef), NULL, false);
mask.contents = (__bridge id)maskImage;
mask.frame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height);
view.layer.mask = mask;
view.layer.masksToBounds = YES;
CGImageRelease(maskImage);

где "scrollMask" - это оттенок серого, определяющий область маски: белый == полностью замаскированный, черный == не замаскирован вообще и серый == частично замаскирован.

Чтобы создать эффект, который вы ищете, изображение маски будет черным с белым градиентом вверху, как это:

enter image description here

Подробнее см. документацию для CGImageMaskCreate.

Ответ 6

Мы построили код Steph Sharp, чтобы при необходимости исчезнуть. Наш код настраивается как статический служебный метод вместо подкласса, поэтому мы можем повторно использовать метод в наших подклассах UIScrollView и UITableView. Вот наш статический метод утилиты:

#define kScrollViewFadeColorLight [UIColor colorWithRed:0.56 green:0.56 blue:0.56 alpha:0.0]
#define kScrollViewFadeColorDark [UIColor colorWithRed:0.56 green:0.56 blue:0.56 alpha:1.0]

#define kScrollViewScrollBarWidth 7.0
#define kScrollViewFadingEdgeLength 40.0

+ (void) applyFadeToScrollView:(UIScrollView*) scrollView {
  CGFloat topOffset = -scrollView.contentInset.top;
  CGFloat bottomOffset = scrollView.contentSize.height + scrollView.contentInset.bottom - scrollView.bounds.size.height;
  CGFloat distanceFromTop = scrollView.contentOffset.y - topOffset;
  CGFloat distanceFromBottom = bottomOffset - scrollView.contentOffset.y;
  BOOL isAtTop = distanceFromTop < 1.0;
  BOOL isAtBottom = distanceFromBottom < 1.0;
  if (isAtTop && isAtBottom) {
    // There is no scrolling to be done here, so don't fade anything!
    scrollView.layer.mask = nil;
    return;
  }
  NSObject* transparent = (NSObject*)[kScrollViewFadeColorLight CGColor];
  NSObject* opaque = (NSObject*)[kScrollViewFadeColorDark CGColor];

  CALayer* maskLayer = [CALayer layer];
  maskLayer.frame = scrollView.bounds;

  CALayer* scrollGutterLayer = [CALayer layer];
  scrollGutterLayer.frame = CGRectMake(scrollView.bounds.size.width - kScrollViewScrollBarWidth, 0.0,
                                       kScrollViewScrollBarWidth, scrollView.bounds.size.height);
  scrollGutterLayer.backgroundColor = (__bridge CGColorRef)(opaque);
  [maskLayer addSublayer:scrollGutterLayer];

  CAGradientLayer* gradientLayer = [CAGradientLayer layer];
  gradientLayer.frame = CGRectMake(0.0, 0.0, scrollView.bounds.size.width, scrollView.bounds.size.height);
  CGFloat fadePercentage = kScrollViewFadingEdgeLength / scrollView.bounds.size.height;
  NSMutableArray* colors = [[NSMutableArray alloc] init];
  NSMutableArray* locations = [[NSMutableArray alloc] init];
  if (!isAtTop) {
    [colors addObjectsFromArray:@[transparent, opaque]];
    [locations addObjectsFromArray:@[@0.0, [NSNumber numberWithFloat:fadePercentage]]];
  }
  if (!isAtBottom) {
    [colors addObjectsFromArray:@[opaque, transparent]];
    [locations addObjectsFromArray:@[[NSNumber numberWithFloat:1.0 - fadePercentage], @1.0]];
  }
  gradientLayer.colors = colors;
  gradientLayer.locations = locations;
  [maskLayer addSublayer:gradientLayer];
  scrollView.layer.mask = maskLayer;
}

Вот наше использование этого метода.

FadingScrollView.h

@interface FadingScrollView : UIScrollView
@end

FadingScrollView.m

@implementation FadingScrollView
- (void) layoutSubviews {
  [super layoutSubviews];
  [Utils applyFadeToScrollView:self];
}
@end

FadingTableView.h

@interface FadingTableView : UITableView    
@end

FadingTableView.m

@implementation FadingTableView
- (void) layoutSubviews {
  [super layoutSubviews];
  [Utils applyFadeToScrollView:self];
}
@end

Ответ 7

Представления - все вложенные представления прокрутки, поэтому каждый из них имеет свой собственный alpha. Вы можете выступать в качестве делегата для просмотра прокрутки, а в scrollViewDidScroll вы можете проверить contentOffset и изменить alpha любого из ваших подсмотров.