web-dev-qa-db-fra.com

UISlider avec ProgressView combiné

Existe-t-il un moyen fabriqué par Apple pour obtenir un UISlider avec ProgressView? Ceci est utilisé par de nombreuses applications de streaming, par exemple. quicktimeplayer natif ou youtube . (Juste pour être sûr: je ne suis intéressé que par la visualisation)

Slider with loader

acclame Simon

36
endo.anaconda

Voici une version simple de ce que vous décrivez.

alt text

C'est "simple" dans le sens où je n'ai pas essayé d'ajouter des nuances et d'autres subtilités. Mais il est facile à construire et vous pouvez le modifier pour dessiner de manière plus subtile si vous le souhaitez. Par exemple, vous pouvez créer votre propre image et l’utiliser comme pouce du curseur.

Il s'agit en fait d'une sous-classe UISlider reposant sur une sous-classe UIView (MyTherm) qui dessine le thermomètre, ainsi que deux étiquettes UIL qui dessinent les nombres. 

La sous-classe UISlider élimine la piste intégrée, de sorte que le thermomètre situé derrière est visible. Mais le curseur (bouton) d'UISlider est toujours déplaçable de manière normale. Vous pouvez le définir comme une image personnalisée, obtenir l'événement Valeur modifiée lorsque l'utilisateur le fait glisser, etc. Voici le code de la sous-classe UISlider qui élimine sa propre piste:

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

Le thermomètre est une instance d'une sous-classe UIView personnalisée, MyTherm. Je l'ai instanciée dans la plume et décoché son opaque et lui ai donné une couleur de fond de couleur claire. Il a une propriété value afin qu'il sache combien il faut remplir le thermomètre. Voici son code 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));
}

Pour modifier la valeur du thermomètre, remplacez la variable value de l'instance MyTherm par un nombre compris entre 0 et 1 et indiquez-lui de se redessiner avec setNeedsDisplay.

47
matt

Ceci est faisable en utilisant les contrôles standard. 

Dans Interface Builder, placez votre UISlider immédiatement au-dessus de votre UIProgressView et donnez-leur la même taille.

Sur une UISlider, la ligne horizontale de fond est appelée la piste, l’astuce consiste à la rendre invisible. Nous faisons cela avec un PNG transparent et les méthodes UISlider, setMinimumTrackImage:forState: et setMaximumTrackImage:forState:.

Dans la méthode viewDidLoad de votre contrôleur de vue, ajoutez:

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

self.slider fait référence à votre UISlider.

J'ai testé le code dans Xcode, ce qui vous donnera un curseur avec une barre de progression indépendante.

20
Sam Doshi

Créez un 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);

   }

Prendre plaisir! P.S. Je pourrais avoir des fautes de frappe.

4
Emile Khattar

La solution qui convient à ma conception:

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)
    }
} 

 

4
Vitalka

Idée 1: Vous pouvez facilement utiliser UISlider en tant que vue de progression en le sous-classant. Il répond à des méthodes telles que 'setValue: animated:' avec lesquelles vous pouvez définir la valeur (c'est-à-dire: la progression) de la vue.

Votre seule "restriction" créant ce que vous voyez dans votre exemple est la barre de mémoire tampon, que vous pouvez créer en personnalisant UISlider de manière "créative" (car vous pouvez y ajouter des apparences personnalisées) et éventuellement la définir par programmation.

Idée 2: Une autre option (plus facile) consiste à sous-classer UIProgressView et à créer un UISlider à l’intérieur de cette sous-classe. Vous pouvez habiller UISlider pour obtenir un habillage transparent (pas de barre, seulement le bouton visible) et le poser sur UIProgressView.

Vous pouvez utiliser UIProgressView pour le préchargement (mise en mémoire tampon) et UISlider pour le contrôle/la progression de la séquence.

Cela semble assez facile :-)

Edit: Pour répondre à votre question, il n’existe pas de solution interne, mais ce serait facile à réaliser avec les outils fournis.

2
Jake

Vous pouvez faire un truc comme ça, c'est plus facile et plus compréhensif. Insérez simplement le code ci-dessous dans votre sous-classe 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" 's length
        _availableDurationImageView.width = _maxLength * availableValue;
    }
}
1
John

Voici une solution dans Objective C. https://github.com/abhimuralidharan/BufferSlider

L'idée est de créer une propriété UIProgressview en tant que propriété de la sous-classe UISlider et d'ajouter les contraintes requises par programme.

#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
0
abhi1992

Ajoutant à la solution de matt, notez qu'à partir de iOS 7.0, implémenter trackRectForBounds: est rendu impossible. Voici ma solution à ce problème:

Dans votre sous-classe UISlider, procédez comme suit:

-(void)awakeFromNib
{
    [super awakeFromNib];

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

avec imageWithColor comme fonction:

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

Cela va correctement prendre soin de ce trackRectangle ennuyeux.

J'ai passé trop de temps à chercher une solution à ce problème, espérant que cela permettra de gagner du temps pour une autre âme pauvre;).

0
CyberDandy