我想知道如何制作一个看起来像 KAYAK 应用程序(这是一个搜索航班和酒店的旅行应用程序)中的进度视图,截图:
我在越狱的 iPhone 上挖掘了 KAYAK 应用程序的资源,发现以下 3 张图像构成了这个进度视图:
进度条-背景@2x.png
进度条渐变@2x.png
进度条覆盖@2x.png
假设进度视图有一个移动的覆盖图像,它与梯度图像一起重复移动。
任何想法或示例代码将不胜感激。
我想知道如何制作一个看起来像 KAYAK 应用程序(这是一个搜索航班和酒店的旅行应用程序)中的进度视图,截图:
我在越狱的 iPhone 上挖掘了 KAYAK 应用程序的资源,发现以下 3 张图像构成了这个进度视图:
进度条-背景@2x.png
进度条渐变@2x.png
进度条覆盖@2x.png
假设进度视图有一个移动的覆盖图像,它与梯度图像一起重复移动。
任何想法或示例代码将不胜感激。
I've made entire working package that will mimic this progress view you have posted. However, to make it more customizable, I have not used any images, but used CoreGraphics to draw it. The package can be found at lightdesign/LDProgressView. I'll also probably make it into a CocoaPod if you know what that is.
All of the inner workings of the progress view and therefore how to mimic the KAYAK progress view can be found in this file. I've added some comments here in the code blocks for easier understanding. Here's the drawRect
method:
- (void)drawRect:(CGRect)rect {
[self setAnimateIfNotSet];
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawProgressBackground:context inRect:rect];
if (self.progress > 0) {
[self drawProgress:context withFrame:rect];
}
}
This is pretty self-explanatory. I set the animate property if it isn't set already and I draw the background. Then, if the progress is greater 0, I'll draw the progress within the total frame. Let's move on to the drawProgressBackground:inRect:
method:
- (void)drawProgressBackground:(CGContextRef)context inRect:(CGRect)rect {
CGContextSaveGState(context);
// Draw the background with a gray color within a rounded rectangle
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:10];
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.51f green:0.51f blue:0.51f alpha:1.00f].CGColor);
[roundedRect fill];
// Create the inner shadow path
UIBezierPath *roundedRectangleNegativePath = [UIBezierPath bezierPathWithRect:CGRectMake(-10, -10, rect.size.width+10, rect.size.height+10)];
[roundedRectangleNegativePath appendPath:roundedRect];
roundedRectangleNegativePath.usesEvenOddFillRule = YES;
CGSize shadowOffset = CGSizeMake(0.5, 1);
CGContextSaveGState(context);
CGFloat xOffset = shadowOffset.width + round(rect.size.width);
CGFloat yOffset = shadowOffset.height;
CGContextSetShadowWithColor(context,
CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)), 5, [[UIColor blackColor] colorWithAlphaComponent:0.7].CGColor);
// Draw the inner shadow
[roundedRect addClip];
CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(rect.size.width), 0);
[roundedRectangleNegativePath applyTransform:transform];
[[UIColor grayColor] setFill];
[roundedRectangleNegativePath fill];
CGContextRestoreGState(context);
}
Here, I create a rounded rectangle within the view with a radius of 10
(which I may later allow to be customizable) and fill it. Then the rest of the code is drawing the inner shadow, which I don't really need to go into detail about. Now, here's the code for drawing the progress in the method drawProgress:withFrame:
:
- (void)drawProgress:(CGContextRef)context withFrame:(CGRect)frame {
CGRect rectToDrawIn = CGRectMake(0, 0, frame.size.width * self.progress, frame.size.height);
CGRect insetRect = CGRectInset(rectToDrawIn, 0.5, 0.5);
UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:10];
if ([self.flat boolValue]) {
CGContextSetFillColorWithColor(context, self.color.CGColor);
[roundedRect fill];
} else {
CGContextSaveGState(context);
[roundedRect addClip];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
NSArray *colors = @[(__bridge id)[self.color lighterColor].CGColor, (__bridge id)[self.color darkerColor].CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGContextDrawLinearGradient(context, gradient, CGPointMake(insetRect.size.width / 2, 0), CGPointMake(insetRect.size.width / 2, insetRect.size.height), 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
CGContextSetStrokeColorWithColor(context, [[self.color darkerColor] darkerColor].CGColor);
[self drawStripes:context inRect:insetRect];
[roundedRect stroke];
[self drawRightAlignedLabelInRect:insetRect];
}
There are 4 primary parts to this method. First, I do the calculation of the frame that the progress will take up based on the self.progress
property. Second, I draw either a solid color if the flat
property is set, or I draw a calculated gradient (methods lighterColor
and darkerColor
are in a UIColor
category). Third, I draw stripes and finally draw the percentage label. Let's cover those 2 methods quickly. Here's the drawStripes:inRect:
method:
- (void)drawStripes:(CGContextRef)context inRect:(CGRect)rect {
CGContextSaveGState(context);
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:10] addClip];
CGContextSetFillColorWithColor(context, [[UIColor whiteColor] colorWithAlphaComponent:0.2].CGColor);
CGFloat xStart = self.offset, height = rect.size.height, width = STRIPE_WIDTH;
while (xStart < rect.size.width) {
CGContextSaveGState(context);
CGContextMoveToPoint(context, xStart, height);
CGContextAddLineToPoint(context, xStart + width * 0.25, 0);
CGContextAddLineToPoint(context, xStart + width * 0.75, 0);
CGContextAddLineToPoint(context, xStart + width * 0.50, height);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
xStart += width;
}
CGContextRestoreGState(context);
}
This is where the animation "magic" happens. Essentially I draw these stripes based off of self.offset
which is somewhere between -STRIPE_WIDTH
and 0
as incremented by a timer. Then, I create a simple loop so that I only create enough stripes to completely fill the progress portion of the view. I also leave 25% of the STRIPE_WIDTH
blank so that the stripes aren't bunched up against each other. Here's the final drawing method drawRightAlignedLabelInRect:
:
- (void)drawRightAlignedLabelInRect:(CGRect)rect {
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentRight;
label.text = [NSString stringWithFormat:@"%.0f%%", self.progress*100];
label.font = [UIFont boldSystemFontOfSize:17];
UIColor *baseLabelColor = [self.color isLighterColor] ? [UIColor blackColor] : [UIColor whiteColor];
label.textColor = [baseLabelColor colorWithAlphaComponent:0.6];
[label drawTextInRect:CGRectOffset(rect, -6, 0)];
}
In this method I create a label with text that is convert from a float (between 0.0
and 1.0
) to a percentage (from 0%
to 100%
). I then either set the color to be dark or light depending on the darkness of the chosen progress color and draw the label in the CGContext
.
There are three properties that can be set either directly on an instance of LDProgressView
or beforehand in a UIAppearance
method.
The color will obviously set the general look of the picker. The gradients, stripes, and/or outline colors are determined off of this. The UIAppearance
method would be something like this:
[[LDProgressView appearance] setColor:[UIColor colorWithRed:0.87f green:0.55f blue:0.09f alpha:1.00f]];
This will determine whether the background of the progress view will be a gradient or just the color
property. This UIAppearance
method would look something like this:
[[LDProgressView appearance] setFlat:@NO];
Finally, this will determine whether the stripes will be animated. This UIAppearance
method can also be generically set for all instances of LDProgressView
and looks like this:
[[LDProgressView appearance] setAnimate:@YES];
Whew! That was a long answer. I hope I didn't bore you guys too much. If you just skipped down to here, here's is the gist for drawing in code rather than with images. I think CoreGraphics is a superior way of drawing on iOS if you have the time/experience since it allows for more customization and I believe tends to be faster.
Here's a picture of the final, working product:
The Closest related Progress Bar in your case is ADVProgressBar.
Here, you need to do a little bit modification. Like, you have to change the Back ground image and you can add those Rounded corners. After those changes, the progress bar will look exactly like what you mentioned in your Question.
Screenshot :
调整颜色和厚度,你就可以开始了! https://www.cocoacontrols.com/controls/ylprogressbar
这个好像也不错……不过我还没用。 https://github.com/JonasGessner/JGProgressView
祝你好运!
您可以使用NSTimer
更新开始渲染叠加层的“偏移量”。你可以在这里找到一个例子。它是 OS X 控件,它使用自定义绘图,而不是图像,但原理是相同的。
当您设置您的UIProgressView
( self.progress
) 我们以下设置以获得相同的外观:
UIImage *mynd =[[UIImage imageNamed:@"KgcoJ.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(11, 13, 11, 13)];
UIImage *bakMynd =[[UIImage imageNamed:@"UoS0A.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(11, 13, 11, 13)];
[self.progress setProgressImage:mynd];
[self.progress setTrackImage:bakMynd];
UIEdgeInsetsMake
请注意,您可能必须根据您的图像尺寸更改图像名称和数字。这将为您提供以下信息:
您可以使用此自定义可可控件
为 ProgressView 尝试 Cocoa Controls ......至于覆盖的图像,您需要使用与 progressview 绝对同步的 NSTimer