UISlider вместе с ProgressView

Есть ли у Apple-дом способ получить UISlider с ProgressView. Это используется многими потоковыми приложениями, например. собственный quicktimeplayer или youtube. (Чтобы быть уверенным: я заинтересован в визуализации)

Slider  with loader

приветствует Саймона

Ответ 1

Вот простая версия того, что вы описываете.

alt text

Это "просто" в том смысле, что я не стал пытаться добавить оттенки и другие тонкости. Но это легко построить, и вы можете настроить его, чтобы сделать более тонким способом, если хотите. Например, вы можете создать собственное изображение и использовать его как большой слайдер.

Это на самом деле подкласс UISlider, лежащий поверх подкласса UIView (MyTherm), который рисует термометр плюс два UILabels, которые рисуют числа.

Подкласс UISlider устраняет встроенную дорожку, так что через нее проходит термометр. Но большой палец UISlider (ручка) по-прежнему перетаскивается обычным способом, и вы можете установить его на пользовательское изображение, получить событие Value Changed, когда пользователь перетащит его и т.д. Вот код для подкласса UISlider, который устраняет собственный трек:

- (CGRect)trackRectForBounds:(CGRect)bounds {
    CGRect result = [super trackRectForBounds:bounds];
    result.size.height = 0;
    return result;
}

Термометр - это экземпляр пользовательского подкласса UIView, MyTherm. Я создал его в банке и снял флажок с Opaque и дал ему цвет фона Clear Color. Он имеет свойство value, поэтому он знает, сколько нужно заполнить термометром. Вот его код drawRect::

- (void)drawRect:(CGRect)rect {
    CGContextRef c = UIGraphicsGetCurrentContext();
    [[UIColor whiteColor] set];
    CGFloat ins = 2.0;
    CGRect r = CGRectInset(self.bounds, ins, ins);
    CGFloat radius = r.size.height / 2.0;
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, CGRectGetMaxX(r) - radius, ins);
    CGPathAddArc(path, NULL, radius+ins, radius+ins, radius, -M_PI/2.0, M_PI/2.0, true);
    CGPathAddArc(path, NULL, CGRectGetMaxX(r) - radius, radius+ins, radius, M_PI/2.0, -M_PI/2.0, true);
    CGPathCloseSubpath(path);
    CGContextAddPath(c, path);
    CGContextSetLineWidth(c, 2);
    CGContextStrokePath(c);
    CGContextAddPath(c, path);
    CGContextClip(c);
    CGContextFillRect(c, CGRectMake(r.origin.x, r.origin.y, r.size.width * self.value, r.size.height));
}

Чтобы изменить значение термометра, измените экземпляр MyTherm value на число от 0 до 1 и скажите ему перерисовать себя с помощью setNeedsDisplay.

Ответ 2

Это можно выполнить с помощью стандартных элементов управления.

В интерфейсе Builder разместите UISlider непосредственно поверх своего UIProgressView и сделайте их одинакового размера.

На UISlider фоновая горизонтальная линия называется дорожкой, трюк должен сделать ее невидимой. Мы делаем это с помощью прозрачных PNG и методов UISlider setMinimumTrackImage:forState: и setMaximumTrackImage:forState:.

В методе viewDidLoad вашего контроллера просмотров добавьте:

[self.slider setMinimumTrackImage:[UIImage imageNamed:@"transparent.png"] forState:UIControlStateNormal];
[self.slider setMaximumTrackImage:[UIImage imageNamed:@"transparent.png"] forState:UIControlStateNormal];

где self.slider относится к вашему UISlider.

Я протестировал код в Xcode, и это даст вам слайдер с независимым индикатором выполнения.

Ответ 3

Создайте UISlider:

// 1
// Make the slider as a public propriety so you can access it
playerSlider = [[UISlider alloc] init];
[playerSlider setContinuous:YES];
[playerSlider setHighlighted:YES];
// remove the slider filling default blue color
[playerSlider setMaximumTrackTintColor:[UIColor clearColor]];
[playerSlider setMinimumTrackTintColor:[UIColor clearColor]];
// Chose your frame
playerSlider.frame = CGRectMake(--- , -- , yourSliderWith , ----);

// 2
// create a UIView that u can access and make it the shadow of your slider 
shadowSlider = [[UIView alloc] init];
shadowSlider.backgroundColor = [UIColor lightTextColor];
shadowSlider.frame = CGRectMake(playerSlider.frame.origin.x , playerSlider.frame.origin.y , playerSlider.frame.size.width , playerSlider.frame.origin.size.height);
shadowSlider.layer.cornerRadius = 4;
shadowSlider.layer.masksToBounds = YES;
[playerSlider addSubview:shadowSlider];
[playerSlider sendSubviewToBack:shadowSlider];


// 3
// Add a timer Update your slider and shadow slider programatically 
 [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateSlider) userInfo:nil repeats:YES];


-(void)updateSlider {

    // Update the slider about the music time
    playerSlider.value = audioPlayer.currentTime; // based on ur case 
    playerSlider.maximumValue = audioPlayer.duration;

    float smartWidth = 0.0;
    smartWidth = (yourSliderFullWidth * audioPlayer.duration ) / 100;
    shadowSlider.frame = CGRectMake( shadowSlider.frame.origin.x , shadowSlider.frame.origin.y , smartWidth , shadowSlider.frame.size.height);

   }

Наслаждайтесь! Постскриптум Возможно, у меня есть опечатки.

Ответ 4

Решение, которое соответствует моему дизайну:

class SliderBuffering:UISlider {
    let bufferProgress =  UIProgressView(progressViewStyle: .Default)

    override init (frame : CGRect) {
        super.init(frame : frame)
    }

    convenience init () {
        self.init(frame:CGRect.zero)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        self.minimumTrackTintColor = UIColor.clearColor()
        self.maximumTrackTintColor = UIColor.clearColor()
        bufferProgress.backgroundColor = UIColor.clearColor()
        bufferProgress.userInteractionEnabled = false
        bufferProgress.progress = 0.0
        bufferProgress.progressTintColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.5)
        bufferProgress.trackTintColor = UIColor.blackColor().colorWithAlphaComponent(0.5)
        self.addSubview(bufferProgress)
    }
} 

Ответ 5

Идея 1: Вы легко можете использовать UISlider в качестве представления прогресса, подклассифицируя его. Он реагирует на такие методы, как "setValue: animated:", с помощью которых вы можете установить значение (то есть прогресс) представления.

Ваше единственное "ограничение", создающее то, что вы видите в вашем примере, представляет собой буферную панель, которую вы можете создать с помощью "творчески" скинирования UISlider (потому что вы можете добавить к ней пользовательские скины) и, возможно, программным образом установите этот скин

Идея 2: Другой (более простой) вариант заключается в подклассе UIProgressView и создании UISlider внутри этого подкласса. Вы можете обрезать UISlider, чтобы иметь прозрачный скин (нет полосы, только ручка видимая) и класть ее поверх UIProgressView.

Вы можете использовать UIProgressView для предварительной загрузки (буферизации) и UISlider для индикации контроля/выполнения видео.

Кажется довольно простым: -)

Изменить:, чтобы на самом деле ответить на ваш вопрос, нет внутреннего способа, но это было бы легко сделать с помощью предоставленных инструментов.

Ответ 6

Вы можете сделать такой трюк, как это, это проще и понятнее. Просто вставьте приведенный ниже код в свой подкласс UISlider.

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (_availableDurationImageView == nil) {
        // step 1 
        // get max length that our "availableDurationImageView" will show
        UIView *maxTrackView = [self.subviews objectAtIndex:self.subviews.count - 3];
        UIImageView *maxTrackImageView = [maxTrackView.subviews objectAtIndex:0];
        _maxLength = maxTrackImageView.width;

        // step 2
        // get the right frame where our "availableDurationImageView" will place in       superView
        UIView *minTrackView = [self.subviews objectAtIndex:self.subviews.count - 2];
        _availableDurationImageView = [[UIImageView alloc] initWithImage:[[UIImage     imageNamed:@"MediaSlider.bundle/4_jindu_huancun.png"]     resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)]];
        _availableDurationImageView.opaque = NO;
        _availableDurationImageView.frame = minTrackView.frame;

        [self insertSubview:_availableDurationImageView belowSubview:minTrackView];
    }
}


- (void)setAvailableValue:(NSTimeInterval)availableValue
{
    if (availableValue >=0 && availableValue <= 1) {
        // use "maxLength" and percentage to set our "availableDurationImageView"  length
        _availableDurationImageView.width = _maxLength * availableValue;
    }
}

Ответ 7

Добавляя матовое решение, обратите внимание, что с iOS 7.0 реализация trackRectForBounds: оказывается невозможной. Вот мое решение этой проблемы:

В подклассе UISlider выполните следующее:

-(void)awakeFromNib
{
    [super awakeFromNib];

    UIImage* clearColorImage = [UIImage imageWithColor:[UIColor clearColor]];
    [self setMinimumTrackImage:clearColorImage forState:UIControlStateNormal];
    [self setMaximumTrackImage:clearColorImage forState:UIControlStateNormal];
}

с помощью функции imageWithColor в качестве этой функции:

+ (UIImage*) imageWithColor:(UIColor*)color
{
    return [UIImage imageWithColor:color andSize:CGSizeMake(1.0f, 1.0f)];
}

Это правильно позаботится об этом раздражающем trackRectangle.

Я потратил слишком много времени на поиск решения этой проблемы, надеясь, что это сэкономит некоторое время другой бедной душе;).

Ответ 8

Вот решение в Objective C. https://github.com/abhimuralidharan/BufferSlider

Идея состоит в том, чтобы создать UIProgressview как свойство в подклассе UISlider и программно добавить необходимые ограничения.

#import <UIKit/UIKit.h> //.h file

@interface BufferSlider : UISlider
@property(strong,nonatomic) UIProgressView *bufferProgress;
@end

#import "BufferSlider.h" //.m file

@implementation BufferSlider


- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}
-(void)setup {
    self.bufferProgress = [[UIProgressView alloc] initWithFrame:self.bounds];
    self.minimumTrackTintColor = [UIColor redColor];
    self.maximumTrackTintColor = [UIColor clearColor];
    self.value = 0.2;
    self.bufferProgress.backgroundColor = [UIColor clearColor];
    self.bufferProgress.userInteractionEnabled = NO;
    self.bufferProgress.progress = 0.7;
    self.bufferProgress.progressTintColor = [[UIColor blueColor] colorWithAlphaComponent:0.5];
    self.bufferProgress.trackTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:2];
    [self addSubview:self.bufferProgress];
    [self setThumbImage:[UIImage imageNamed:@"redThumb"] forState:UIControlStateNormal];
    self.bufferProgress.translatesAutoresizingMaskIntoConstraints = NO;

    NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.bufferProgress attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
    NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:self.bufferProgress attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0.75];  // edit the constant value based on the thumb image
    NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.bufferProgress attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];

    [self addConstraints:@[left,right,centerY]];
    [self sendSubviewToBack:self.bufferProgress];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self setup];
    }
    return self;
}
@end