UISlider with ProgressView combined

后端 未结 9 1358
无人共我
无人共我 2020-11-29 16:07

Is there an apple-house-made way to get a UISlider with a ProgressView. This is used by many streaming applications e.g. native quicktimeplayer or youtube. (Just to be sure:

相关标签:
9条回答
  • 2020-11-29 16:45

    You can do some trick like this, it's more easy and understanding. Just insert the code bellow in your UISlider subclass.

    - (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;
        }
    }
    
    0 讨论(0)
  • 2020-11-29 16:49

    Solution that suits my design:

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

    0 讨论(0)
  • 2020-11-29 16:55

    Idea 1: You could easily use the UISlider as a progress view by subclassing it. It responds to methods such as 'setValue:animated:' with which you can set the value (i.e: progress) of the view.

    Your only 'restriction' creating what you see in your example is the buffer bar, which you could create by 'creatively' skinning the UISlider (because you can add custom skins to it), and perhaps set that skin programmatically.

    Idea 2: Another (easier) option is to subclass UIProgressView, and create a UISlider inside that subclass. You can skin the UISlider to have a see-through skin (no bar, just the knob visible) and lay it over the UIProgressView.

    You can use the UIProgressView for the pre-loading (buffering) and the UISlider for movie control / progress indication.

    Seems fairly easy :-)

    Edit: to actually answer your question, there is no in-house way, but it would be easy to accomplish with the tools given.

    0 讨论(0)
  • 2020-11-29 16:58

    Adding on matt's solution, note that as of iOS 7.0, implementing trackRectForBounds: is rendered impossible. Here is my solution to this problem :

    In your UISlider subclass, do this :

    -(void)awakeFromNib
    {
        [super awakeFromNib];
    
        UIImage* clearColorImage = [UIImage imageWithColor:[UIColor clearColor]];
        [self setMinimumTrackImage:clearColorImage forState:UIControlStateNormal];
        [self setMaximumTrackImage:clearColorImage forState:UIControlStateNormal];
    }
    

    with imageWithColor as this function :

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

    That will properly take care of this annoying trackRectangle.

    I spent too much time looking for a solution to this problem, here's hoping that'll save some time to another poor soul ;).

    0 讨论(0)
  • 2020-11-29 17:03

    Here's a simple version of what you're describing.

    alt text

    It is "simple" in the sense that I didn't bother trying to add the shading and other subtleties. But it's easy to construct and you can tweak it to draw in a more subtle way if you like. For example, you could make your own image and use it as the slider's thumb.

    This is actually a UISlider subclass lying on top of a UIView subclass (MyTherm) that draws the thermometer, plus two UILabels that draw the numbers.

    The UISlider subclass eliminates the built-in track, so that the thermometer behind it shows through. But the UISlider's thumb (knob) is still draggable in the normal way, and you can set it to a custom image, get the Value Changed event when the user drags it, and so on. Here is the code for the UISlider subclass that eliminates its own track:

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

    The thermometer is an instance of a custom UIView subclass, MyTherm. I instantiated it in the nib and unchecked its Opaque and gave it a background color of Clear Color. It has a value property so it knows how much to fill the thermometer. Here's its drawRect: code:

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

    To change the thermometer value, change the MyTherm instance's value to a number between 0 and 1, and tell it to redraw itself with setNeedsDisplay.

    0 讨论(0)
  • 2020-11-29 17:03

    Create a 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);
    
       }
    

    Enjoy! P.S. I might have some typos.

    0 讨论(0)
提交回复
热议问题