【问题标题】:how to make a progress view like the one in KAYAK app如何制作类似于 KAYAK 应用程序中的进度视图
【发布时间】:2013-09-19 05:58:56
【问题描述】:

我想知道如何制作一个看起来像 KAYAK 应用程序(这是一个搜索航班和酒店的旅行应用程序)中的进度视图,截图:

我在越狱的 iPhone 上挖掘了 KAYAK 应用程序的资源,发现以下 3 张图片构成了这个进度视图:

progressbar-background@2x.png

progressbar-gradient@2x.png

progressbar-overlay@2x.png

假设进度视图有一个移动的叠加图像,它会随着渐变图像重复移动。

任何想法或示例代码都将受到高度赞赏。

【问题讨论】:

  • 查看 - (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets。这与任何 github 建议应该是可行的。

标签: iphone ios objective-c uiprogressview


【解决方案1】:

我已经制作了完整的工作包,可以模仿您发布的这个进度视图。但是,为了使其更可定制,我没有使用任何图像,而是使用 CoreGraphics 来绘制它。可以在lightdesign/LDProgressView 找到该软件包。如果你知道那是什么,我也可能会将它变成 CocoaPod

如何绘制类似 KAYAK 的进度视图

进度视图的所有内部工作原理以及如何模仿 KAYAK 进度视图可以在this file 中找到。为了便于理解,我在代码块中添加了一些 cmets。这是drawRect 方法:

- (void)drawRect:(CGRect)rect {
    [self setAnimateIfNotSet];
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self drawProgressBackground:context inRect:rect];
    if (self.progress > 0) {
        [self drawProgress:context withFrame:rect];
    }
}

这是不言自明的。如果尚未设置动画属性,我将设置它并绘制背景。然后,如果进度大于0,我将在总帧内绘制进度。让我们继续 drawProgressBackground:inRect: 方法:

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

在这里,我在视图中创建一个圆角矩形,半径为10(我以后可能允许对其进行自定义)并填充它。然后剩下的代码是绘制内阴影,我真的不需要详细说明。现在,这是在方法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];
}

此方法有 4 个主要部分。首先,我根据self.progress 属性计算进度将占用的帧。其次,如果设置了flat 属性,我绘制纯色,或者绘制计算渐变(方法lighterColordarkerColor 属于UIColor 类别)。第三,我画条纹,最后画百分比标签。让我们快速介绍这两种方法。这是drawStripes:inRect: 方法:

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

这就是动画“魔法”发生的地方。本质上,我根据self.offset 绘制这些条纹,它介于-STRIPE_WIDTH0 之间,由计时器递增。然后,我创建了一个简单的循环,这样我只创建了足够的条纹来完全填充视图的进度部分。我还将 STRIPE_WIDTH 的 25% 留空,这样条纹就不会相互堆积。下面是最终的绘制方法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)];
}

在这种方法中,我创建了一个带有文本的标签,该标签从浮点数(在0.01.0 之间)转换为百分比(从0%100%)。然后我根据所选进度颜色的暗度将颜色设置为暗或亮,并在CGContext 中绘制标签。

可定制性

可以直接在LDProgressView 的实例上或在UIAppearance 方法中预先设置三个属性。

  • 颜色

颜色显然会设置选择器的总体外观。渐变、条纹和/或轮廓颜色由此确定。 UIAppearance 方法是这样的:

 [[LDProgressView appearance] setColor:[UIColor colorWithRed:0.87f green:0.55f blue:0.09f alpha:1.00f]];

这将决定进度视图的背景是渐变还是color 属性。这个UIAppearance 方法看起来像这样:

[[LDProgressView appearance] setFlat:@NO];
  • 动画

最后,这将决定条纹是否会被动画化。这个UIAppearance 方法也可以为LDProgressView 的所有实例通用设置,如下所示:

[[LDProgressView appearance] setAnimate:@YES];

结论

哇!那是一个很长的答案。我希望我没有让你们太无聊。如果您只是跳到这里,这里是使用代码而不是图像绘制的要点。如果您有时间/经验,我认为 CoreGraphics 是一种在 iOS 上绘图的好方法,因为它允许更多的自定义,而且我相信往往会更快。

这是最终工作产品的图片:

【讨论】:

  • 我已经从 Github 下载了你的项目,但动画根本不起作用,我尝试用 IBOutlet 连接进度视图,然后用 setAnimate 启动动画,但无济于事.. . 代码中是否缺少某些东西才能正常工作?
  • 我已经更新了……我会专门测试一个 IBOutlet 看看我是否有问题。 更新:我用 IBOutlet 测试了最新版本,它工作正常。试一试,看看还是不行。
  • 非常感谢我现在的工作,但控制台中反复出现错误提示:: clip: empty path , 如何消除这个烦人的错误?
  • 好的,我已经修复了路径错误。我的代码中不小心多了一个CGContextClip 函数。
  • 您需要将 UIView 拖到控制器上,然后将其类更改为 LDProgressView。最后,我会在您的代码中添加一个IBOutlet 并在那里配置属性。希望这会有所帮助!
【解决方案2】:

您可以使用NSTimer 更新开始渲染叠加层的“偏移量”。你可以找到一个例子here。是OS X控件,使用自定义绘图,不是图片,但原理是一样的。

【讨论】:

【解决方案3】:

调整颜色和厚度,你就可以开始了! https://www.cocoacontrols.com/controls/ylprogressbar

这个好像也不错……不过我还没用过。 https://github.com/JonasGessner/JGProgressView

祝你好运!

【讨论】:

    【解决方案4】:

    这是一些示例,您可以根据需要修改一些代码并获取进度视图: 检查Example1Example2

    【讨论】:

      【解决方案5】:

      在您的案例中,最接近的相关进度条是 ADVProgressBar

      在这里,您需要进行一些修改。就像,你必须改变背景图像,你可以添加那些圆角。进行这些更改后,进度条将与您在问题中提到的完全一样。


      截图:

      【讨论】:

        【解决方案6】:

        你可以使用这个自定义的可可控件

        ADV control

        【讨论】:

        • 这对您有帮助吗??
        • 不满足问题要求
        • 为什么不符合您的要求?
        • 我需要带有覆盖框架的进度视图的移动行为,下载 Kayak 应用程序并尝试一下
        【解决方案7】:

        为 ProgressView 尝试 Cocoa Controls....... 至于覆盖图像,您需要使用与 progressview 绝对同步中继的 NSTimer

        【讨论】:

          【解决方案8】:

          当您设置您的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 中的图像名称和数字。 这将为您提供以下信息:

          【讨论】:

            猜你喜欢
            • 2018-01-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-25
            相关资源
            最近更新 更多