Skip to content

Instantly share code, notes, and snippets.

@JohnEstropia
Last active October 5, 2023 14:59
Show Gist options
  • Save JohnEstropia/9482567 to your computer and use it in GitHub Desktop.
Save JohnEstropia/9482567 to your computer and use it in GitHub Desktop.
Workaround an annoying bug with iOS 7.1 where custom track and progress images for UIProgressView gets ignored
//
// JEProgressView.h
//
//
// Created by John Rommel Estropia on 2014/03/11.
// Copyright (c) 2014 John Rommel Estropia.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <UIKit/UIKit.h>
// iOS 7.1 bugfix
// http://stackoverflow.com/questions/22311516/uiprogressview-custom-track-and-progress-images-in-ios-7-1
@interface JEProgressView : UIProgressView
@end
//
// JEProgressView.m
//
//
// Created by John Rommel Estropia on 2014/03/11.
// Copyright (c) 2014 John Rommel Estropia.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "JEProgressView.h"
@interface JEProgressView ()
@property (nonatomic, weak) UIImageView *trackImageView;
@property (nonatomic, weak) UIImageView *progressImageView;
@end
@implementation JEProgressView
#pragma mark - NSObject
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setupProgressView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setupProgressView];
}
return self;
}
#pragma mark - UIView
- (void)layoutSubviews
{
[super layoutSubviews];
UIImageView *trackImageView = self.trackImageView;
UIImageView *progressImageView = self.progressImageView;
if (!trackImageView || !progressImageView)
{
return;
}
CGRect bounds = self.bounds;
CGFloat boundsTop = CGRectGetMinY(bounds);
UIImage *trackImage = self.trackImage;
if (trackImage)
{
CGRect trackFrame = trackImageView.frame;
CGFloat trackHeight = trackImage.size.height;
trackImageView.frame = (CGRect){
.origin.x = CGRectGetMinX(trackFrame),
.origin.y = (boundsTop
+ ((CGRectGetHeight(bounds) - trackHeight) * 0.5f)),
.size.width = CGRectGetWidth(trackFrame),
.size.height = trackHeight
};
}
UIImage *progressImage = self.progressImage;
if (progressImage)
{
CGRect progressFrame = progressImageView.frame;
CGFloat progressHeight = progressImage.size.height;
progressImageView.frame = (CGRect){
.origin.x = CGRectGetMinX(progressFrame),
.origin.y = (boundsTop
+ ((CGRectGetHeight(bounds) - progressHeight) * 0.5f)),
.size.width = CGRectGetWidth(progressFrame),
.size.height = progressHeight
};
}
}
#pragma mark - UIProgressView
- (void)setProgressImage:(UIImage *)progressImage
{
[super setProgressImage:progressImage];
self.progressImageView.image = progressImage;
}
- (void)setTrackImage:(UIImage *)trackImage
{
[super setTrackImage:trackImage];
self.trackImageView.image = trackImage;
}
#pragma mark - private
- (void)setupProgressView
{
if ([self compareVersionString:[UIDevice currentDevice].systemVersion
withVersionString:@"7.1"] == NSOrderedAscending)
{
return;
}
NSArray *subviews = self.subviews;
if ([subviews count] != 2)
{
return;
}
for (UIView *subview in subviews)
{
if (![subview isKindOfClass:[UIImageView class]])
{
return;
}
}
self.trackImageView = subviews[0];
self.progressImageView = subviews[1];
self.trackImageView.image = self.trackImage;
self.progressImageView.image = self.progressImage;
}
- (NSComparisonResult)compareVersionString:(NSString *)versionString1
withVersionString:(NSString *)versionString2
{
NSArray *components1 = [versionString1 componentsSeparatedByString:@"."];
NSArray *components2 = [versionString2 componentsSeparatedByString:@"."];
NSUInteger components1Count = [components1 count];
NSUInteger components2Count = [components2 count];
NSUInteger partCount = MAX(components1Count, components2Count);
for (NSInteger part = 0; part < partCount; ++part)
{
if (part >= components1Count)
{
return NSOrderedAscending;
}
if (part >= components2Count)
{
return NSOrderedDescending;
}
NSString *part1String = components1[part];
NSString *part2String = components2[part];
NSInteger part1 = [part1String integerValue];
NSInteger part2 = [part2String integerValue];
if (part1 > part2)
{
return NSOrderedDescending;
}
if (part1 < part2)
{
return NSOrderedAscending;
}
}
return NSOrderedSame;
}
@end
@JohnEstropia
Copy link
Author

@httpsgithub
I haven't tested this, but does the default UIProgressView ignore tintColor when using custom images? If so I'll update the code. Our project clears out tintColor explicitly so I didn't notice.

@mrga
I'm intending to add this to my JEToolkit repository which uses MIT license. I added the license statement in the .h and .m files for now.

@RyoAbe
MITライセンスです。.hと.mに入れておきました。

@JackTheNoob
Copy link

@JohnEstropia
It works,but not works perfectly.
Here is a problem I met:
Present a JEProgressView on a controller's view.Then show a alertview (or a new window). The JEProgressView's apperance changes.progressTintColor turns to blue. (Sorry about my poor English,hope you can catch my meaning)

@mtancock
Copy link

It seems (in my trial) that it doesn't work when setting a progressview up in a storyboard - the setProgressImage/TrackImage methods don't get called. If I set it up in code, those methods get called. (And I have changed the class of the progressView in the storyboard to the JEProgressView)

Cheers for your work on this!!

@JohnEstropia
Copy link
Author

@JackTheNoob
as @httpsgithub mentioned above, you seem to also need to set
self.tintColor = [UIColor clearColor];
so your image doesn't turn blue. I'd automatically set the tintColor to clearColor if the images are not null, but I don't want to change the behavior of the original UIProgressView.

@mtancock
Probably because images in storyboards are set straight within -[initWithCoder:]. You did raise a common use case so I'll include a fix for this.

@maxgribov
Copy link

Problem: AlertView drops custom image from JEProgressView's ProgressImage and change it on default values (plane color). By the way TrackImage is ok.

@oklimberg
Copy link

Hi,

thanks for this fix. It totally fits my needs in a project.
Could you explain, why you calculate the origin on the y-axis of the image views the way you are doing it? With your original Code, the two imageViews will have an origin of -7.25 using images with a height of 17px, cause the heigth of the UIProgressView itself is 2,5px, even though I set a frame with a height of 17px.

When I change the line

.origin.y = (boundsTop + ((CGRectGetHeight(bounds) - trackHeight) * 0.5f)),

to

.origin.y = CGRectGetMinY(trackFrame),

the progress image view is positioned as expected. (I do not use an image for the track)

Regards
Oliver

@JohnEstropia
Copy link
Author

@oklimberg: My computation makes it so that the imageViews are vertically centered within the parent UIProgressView.

@timstudt
Copy link

works great.

Note: height of progressView is still set to 2., so remember to set progressBar.clipsToBounds=NO

@berikv
Copy link

berikv commented Dec 8, 2014

Why not just

- (void)drawRect:(CGRect)rect {
    double progress = MIN(1, MAX(0, self.progress));

    [self.progressBackgroundImage drawInRect:rect];

    CGRect progressFrame = rect;
    progressFrame.size.width = round(rect.size.width * progress);

    [self.progressImage drawInRect:progressFrame];
}

and not subclass UIProgressView?

@karnakar
Copy link

karnakar commented Feb 6, 2015

Hi,
I have been searching for the same solution for a long time, thanks. Can i make progress view round cornered at the corners.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment