web-dev-qa-db-ja.com

UIProgressViewの角を丸くする

次のプロパティを使用してUIProgressViewを作成しました

progressView.progressTintColor = UIColor.appChallengeColorWithAlpha(1.0)
progressView.trackTintColor = UIColor.clearColor()
progressView.clipsToBounds = true
progressView.layer.cornerRadius = 5

ボーダーにUIViewを使用しています。彼の進捗状況= 1のように見えます。これはまさに私が望む方法です。

enter image description here

ただし、進行状況の値が1未満の場合、コーナーは丸められません。

enter image description here

何か不足していますか?どうすれば角を丸くできますか?

11
Umair Afzal

検索して試した後、独自のカスタム進行状況ビューを作成することにしました。これは、同じ問題でselevsを見つけた人のためのコードです。

import Foundation
import UIKit

class CustomHorizontalProgressView: UIView {
var progress: CGFloat = 0.0 {

    didSet {
        setProgress()
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)

    setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    setup()
}

func setup() {
    self.backgroundColor = UIColor.clearColor()
}

override func drawRect(rect: CGRect) {
    super.drawRect(rect)

    setProgress()
}

func setProgress() {
    var progress = self.progress
    progress = progress > 1.0 ? progress / 100 : progress

    self.layer.cornerRadius = CGRectGetHeight(self.frame) / 2.0
    self.layer.borderColor = UIColor.grayColor().CGColor
    self.layer.borderWidth = 1.0

    let margin: CGFloat = 6.0
    var width = (CGRectGetWidth(self.frame) - margin)  * progress
    let height = CGRectGetHeight(self.frame) - margin

    if (width < height) {
        width = height
    }

    let pathRef = UIBezierPath(roundedRect: CGRect(x: margin / 2.0, y: margin / 2.0, width: width, height: height), cornerRadius: height / 2.0)

    UIColor.redColor().setFill()
    pathRef.fill()

    UIColor.clearColor().setStroke()
    pathRef.stroke()

    pathRef.closePath()

    self.setNeedsDisplay()
}
}

上記のコードをSwiftファイルに入れ、UIViewにUIViewをドラッグアンドドロップして、クラスCustomHorizontalProgressViewを指定します。これで完了です。

5
Umair Afzal

UIProgressViewには、進行状況と追跡の2つの部分があります。 Revealを使用すると、2つのサブビューしかないことがわかります。進行状況ビューの階層は非常に単純です。そう...

Objective-C

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self.progressView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        obj.layer.masksToBounds = YES;
        obj.layer.cornerRadius = kProgressViewHeight / 2.0;
    }];
}

Swift

override func layoutSubviews() {
    super.layoutSubviews()
    subviews.forEach { subview in
        subview.layer.masksToBounds = true
        subview.layer.cornerRadius = kProgressViewHeight / 2.0
    }
}

サブクラスを認めるか、進行状況ビューを拡張することをお勧めします。あなたがそのような単純な効果のためにそれをしたくない場合、これはトリックをするかもしれません。 Appleがビューの階層を変更し、問題が発生する可能性がある状況に留意してください。

19
ooops

答えるのはとても遅いですが、実際には同じ問題がありました。

ここに私の最も簡単な解決策(コードは必要ありません!):

  1. 進行状況ビューを埋め込むコンテナーを追加する

Container

  1. コンテナの丸い角(10 =コンテナの高さ/ 2)

RoundCorner

  1. 結果 :)

Tada!

3
Jordan Montel

Initでこれを行うだけ

    layer.cornerRadius = *desired_corner_radius*
    clipsToBounds = true
2
Manicek

はい、1つの見落としがあります...コーナー半径がprogressviewに設定され、期待どおりに反映されています。

ただし、トラックイメージを丸める場合は、進行状況ビューをカスタマイズする必要があります。角が丸い画像を使用する必要があります。

[progressView setTrackImage:[UIImage imageNamed:@"roundedTrack.png"]];
//roundedTrack.png must be of rounded corner

この上のコードは、progressviewのtrackViewのイメージを変更するのに役立ちます。

画像が不適切に引き伸ばされる可能性があります。画像のサイズを変更できるようにする必要があります。問題が発生した場合、以下のリンクが役立つ場合があります https://www.natashatherobot.com/ios-stretchable-button-uiedgeinsetsmake/

2
Dev_Tandel
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let v = ProgessView(frame: CGRect(x: 20, y: 200, width: 100, height: 10))
        view.addSubview(v)

        //v.progressLayer.strokeEnd = 0.8

    }
}

class ProgessView: UIView {

    lazy var progressLayer: CAShapeLayer = {
        let line = CAShapeLayer()
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 5, y: 5))
        path.addLine(to: CGPoint(x: self.bounds.width - 5, y: 5))
        line.path = path.cgPath
        line.lineWidth = 6
        line.strokeColor = UIColor(colorLiteralRed: 127/255, green: 75/255, blue: 247/255, alpha: 1).cgColor
        line.strokeStart = 0
        line.strokeEnd = 0.5
        line.lineCap = kCALineCapRound
        line.frame = self.bounds
        return line
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = UIColor.white
        layer.cornerRadius = 5
        layer.borderWidth = 1
        layer.borderColor = UIColor(colorLiteralRed: 197/255, green: 197/255, blue: 197/255, alpha: 1).cgColor
        layer.addSublayer(progressLayer)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

コードをテストします。高さと幅は自由にデザインできます。 strokeEndを使用して、progressViewの進行状況を変更できます。アニメーションを追加できます。しかし、実際にはすでにアニメート可能です。strokeEndの値を変更して、その主な効果を確認できます。独自のアニメーションをデザインしたい場合。以下のようにCATransactionを試してください。

func updateProgress(_ progress: CGFloat) {
        CATransaction.begin()
        CATransaction.setAnimationDuration(3)
        progressLayer.strokeEnd = progress
        CATransaction.commit()
    }
2
季亨达

基本的にプログレスビューの(デフォルトスタイル)サブビューは2つの画像ビューで構成されています。 1つは「進行状況」用、もう1つは「トラック」用です。進行状況ビューのサブビューをループして、画像ビューの各コーナー半径を設定できます。

for let view: UIView in self.progressView.subviews {
    if view is UIImageView {
        view.clipsToBounds = true
        view.layer.cornerRadius = 15
    }
}
1
rgerioda

私はこれとまったく同じ問題を抱えていました。それが、狂ったようにググリングした後であなたの質問に私を導いたものです。問題は2つあります。最初に、プログレスバーの内側を最後に丸める方法(季亨达の答えはその方法を示しています)、次に、追加したCAShapeLayerの丸い端を元のプログレスの正方形の端と一致させる方法その下のバー(この他のStackOverflow質問への回答はそれを助けました Swiftでオブジェクトの正確なポイントを取得する方法? )季亨达のこのコード行を置き換える場合:

path.addLine(to:CGPoint(x:self.bounds.width-5、y:5))

これとともに:

path.addLine(to:CGPoint(x:(Int(self.progress * Float(self.bounds.width)))、y:5))

うまくいけば、探している結果が得られます。

0
tonicapote

mair AfzalのソリューションからのSwift 4.2バージョン

class CustomHorizontalProgressView: UIView {

var strokeColor: UIColor?

var progress: CGFloat = 0.0 {
    didSet {
        setNeedsDisplay()
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setup()
}

func setup() {
    self.backgroundColor = UIColor.clear
}

override func draw(_ rect: CGRect) {
    super.draw(rect)
    setProgress()
}

func setProgress() {

    var progress = self.progress
    progress = progress > 1.0 ? progress / 100 : progress

    self.layer.cornerRadius = frame.size.height / 2.0
    self.layer.borderColor = UIColor.gray.cgColor
    self.layer.borderWidth = 1.0

    let margin: CGFloat = 6.0
    var width = (frame.size.width - margin)  * progress
    let height = frame.size.height - margin

    if (width < height) {
        width = height
    }

    let pathRef = UIBezierPath(roundedRect: CGRect(x: margin / 2.0, y: margin / 2.0, width: width, height: height), cornerRadius: height / 2.0)

    strokeColor?.setFill()

    pathRef.fill()
    UIColor.clear.setStroke()
    pathRef.stroke()
    pathRef.close()
}
}

そしてそれを使うには

var progressView: CustomHorizontalProgressView = {
    let view = CustomHorizontalProgressView()
    view.strokeColor = UIColor.orange
    view.progress = 0.5
    return view
}()
0
Kelvin Fok

// Swift 4の更新

import Foundation
import UIKit

class CustomHorizontalProgressView: UIView {
var progress: CGFloat = 0.0 {

    didSet {
        setProgress()
    }
}

override init(frame: CGRect) {
    super.init(frame: frame)

    setup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    setup()
}

func setup() {
    self.backgroundColor = UIColor.clear
}

override func draw(_ rect: CGRect) {
    super.draw(rect)

    setProgress()
}

func setProgress() {
    var progress = self.progress
    progress = progress > 1.0 ? progress / 100 : progress

    self.layer.cornerRadius = self.frame.height / 2.0
    self.layer.borderColor = UIColor.gray.cgColor
    self.layer.borderWidth = 1.0

    let margin: CGFloat = 6.0
    var width = (self.frame.width - margin)  * progress
    let height = self.frame.height - margin

    if (width < height) {
        width = height
    }

    let pathRef = UIBezierPath(roundedRect: CGRect(x: margin / 2.0, y: margin / 2.0, width: width, height: height), cornerRadius: height / 2.0)

    UIColor.red.setFill()
    pathRef.fill()

    UIColor.clear.setStroke()
    pathRef.stroke()

    pathRef.close()

    self.setNeedsDisplay()
 }
}
0
Amit

ミックスを投入するもう1つの答えは、非常にハッキーですが、すぐに使用できます。

サブレイヤーをつかんで半径を設定するだけです。独自のUIProgressViewを記述したり、クリップパスで混乱させる必要はありません。

progressView.layer.cornerRadius = 5
progressView.layer.sublayers[1].cornerRadius = 5
progressView.subviews[1]. clipsToBounds = true
progressView.layer.masksToBounds = true

したがって、全体的なUIProgressViewの角を丸めます(ClipsToBoundsは不要です)次に、塗りつぶしバーは2番目のサブレイヤーなので、それをつかんでその角を丸めることができますが、そのレイヤーのサブビューをclipsToBoundsに設定する必要もあります。

次に、レイヤー全体をその境界にマスクするように設定します。

明らかに、これは大規模 UIProgressViewの設定が変更されず、2番目のサブビュー/レイヤーが塗りつぶしビューであることに依存しています。

だが。その仮定に満足している場合は、非常に簡単なコードを使用してください。

0
Iain Stanford

Swift 4.を使用して、次のようにしています。

let progressViewHeight: CGFloat = 4.0

// Set progress view height
let transformScale = CGAffineTransform(scaleX: 1.0, y: progressViewHeight)
self.progressView.transform = transformScale

// Set progress round corners
self.progressView.layer.cornerRadius = progressViewHeight
self.progressView.clipsToBounds = true
0
Luca Davanzo