Есть ли у Apple-дом способ получить UISlider с ProgressView. Это используется многими потоковыми приложениями, например. собственный quicktimeplayer или youtube. (Чтобы быть уверенным: я заинтересован в визуализации)
приветствует Саймона
Есть ли у Apple-дом способ получить UISlider с ProgressView. Это используется многими потоковыми приложениями, например. собственный quicktimeplayer или youtube. (Чтобы быть уверенным: я заинтересован в визуализации)
приветствует Саймона
Вот простая версия того, что вы описываете.
Это "просто" в том смысле, что я не стал пытаться добавить оттенки и другие тонкости. Но это легко построить, и вы можете настроить его, чтобы сделать более тонким способом, если хотите. Например, вы можете создать собственное изображение и использовать его как большой слайдер.
Это на самом деле подкласс 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
.
Это можно выполнить с помощью стандартных элементов управления.
В интерфейсе 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, и это даст вам слайдер с независимым индикатором выполнения.
Создайте 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);
}
Наслаждайтесь! Постскриптум Возможно, у меня есть опечатки.
Решение, которое соответствует моему дизайну:
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)
}
}
Идея 1: Вы легко можете использовать UISlider в качестве представления прогресса, подклассифицируя его. Он реагирует на такие методы, как "setValue: animated:", с помощью которых вы можете установить значение (то есть прогресс) представления.
Ваше единственное "ограничение", создающее то, что вы видите в вашем примере, представляет собой буферную панель, которую вы можете создать с помощью "творчески" скинирования UISlider (потому что вы можете добавить к ней пользовательские скины) и, возможно, программным образом установите этот скин
Идея 2: Другой (более простой) вариант заключается в подклассе UIProgressView и создании UISlider внутри этого подкласса. Вы можете обрезать UISlider, чтобы иметь прозрачный скин (нет полосы, только ручка видимая) и класть ее поверх UIProgressView.
Вы можете использовать UIProgressView для предварительной загрузки (буферизации) и UISlider для индикации контроля/выполнения видео.
Кажется довольно простым: -)
Изменить:, чтобы на самом деле ответить на ваш вопрос, нет внутреннего способа, но это было бы легко сделать с помощью предоставленных инструментов.
Вы можете сделать такой трюк, как это, это проще и понятнее. Просто вставьте приведенный ниже код в свой подкласс 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;
}
}
Добавляя матовое решение, обратите внимание, что с 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.
Я потратил слишком много времени на поиск решения этой проблемы, надеясь, что это сэкономит некоторое время другой бедной душе;).
Вот решение в 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