rb2030
rb2030

Reputation: 439

UIProgressView setting to an image? UIprogressView entire contentView, downwards?

Image of desired behaviour (In Swift)

Hi there,

I’ve been trying to solve a problem of how to create two progressViews in my viewController for the past week with little to no success. Please see the diagram I have attached as an idea as to the question I am asking.

I am creating a game with a timer which elapses with each question. As the time elapses I would like a UIProgressView to cascade down the entire screen transitioning from blue to white as it goes (Please see number 1 in the diagram, this indicates the white trackTintColor).

Number 2 in the diagram (the cyan part) represents the progressTintColor. The diagram should hopefully be clear in that I am hoping to customise the progressView so that it tracks downwards. Which is one of the main issues at the moment. (I can only seem to fins walkthroughs with small customisable Progressviews which move sideways not up and down)

The hardest part of what I am trying to achieve is (number 3 in the diagram), customisizing a UIImageView, so that it slowly drains downward with the inverse colours to the background (so the dog will be white with cyan flooding downwards to coincide with the progressView elapsing behind it).

These are the options I have tried to solve this issue, thus far, to no avail.

I have tried using a progressView for the backgroundColor, but I cannot find any examples anywhere of anyone else doing this (downwards)so I’m not sure if it’s even possible?

For the image I have tried drawing a CAShape layer but it appears the dog is too difficult a shape for a novice like myself to draw effectively. And upon realising that I would not be able to set a layer of a different colour behind the dog to move downwards as the screen will also be changing color I abandoned all hope of using this option.

I have tried a UIView transition for the dog image, however, the only option I could find that was anywhere close was the transitionCrossDissolve which did not give the downward effect I was hoping for, but instead just faded from a white dog to a cyan dog which was not appropriate. Should I somehow be using progressImage? If so, is there anywhere I can find help with the syntax for that? I can’t seem to find any anywhere.

I currently have 55 images in my assets folder, each with slightly more cyan in than the last, progressively moving downwards (as it animates through an array of the images). Although this works, it is not exactly seamless and does look a little like the user is waiting for an image to load on dial up.

If anyone has any ideas or could spare the time to walk me through how I would go about doing this I would very much appreciate it. I am very much still a beginner so the more detail the better! Oh yes to make matters more difficult, so far I have managed to do the app programatically, so an answers in this form would be great.

Thanks in advance!

Upvotes: 1

Views: 573

Answers (3)

rb2030
rb2030

Reputation: 439

Ok so using McDonal_11's answer I have managed to get the progressView working however, I am still experiencing some problems. I cannot add anything on top of the progressView, it just blanket covers everything else underneath, and before the dog begins its animation into a cyan dog there is a brief flash of the entire cyan dog image.

Code below

private let contentView = UIView(frame: .zero)
private let backgroundImageView = UIImageView(frame: .zero)
private let progressView = ProgressView(frame: .zero)
private let clearViewOverProgress = UIView(frame: .zero)
private let blackDogView = UIView(frame: .zero)
private let blackDogViewImage = UIImageView(frame: .zero)
private let cyanDogView = UIView(frame: .zero)
private let cyanDogViewImage = UIImageView()

var timer = Timer()
var startHeight : CGFloat = 0.0

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    self.progressView.setProgress(10.0, animated: true)
    startHeight = cyanDogViewImage.frame.height
    self.cyanDogViewImage.frame.size.height = 0
    self.timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.updateImage), userInfo: nil, repeats: true)
}


override func viewDidLoad() {
    super.viewDidLoad()
    setupViews()

}

func setupViews() {

self.view.addSubview(backgroundImageView)
    self.backgroundImageView.addSubview(progressView)
    self.progressView.addSubview(clearViewOverProgress)
    self.clearViewOverProgress.addSubview(blackDogView)
    self.blackDogView.addSubview(blackDogViewImage)
    self.blackDogViewImage.addSubview(cyanDogView)
    self.cyanDogView.addSubview(cyanDogViewImage)

// Setting up constraints of both labels (has been omitted for brevity)

    self.blackDogViewImage.image = UIImage(named: “BlackDogImage”)

self.cyanDogViewImage.contentMode = UIViewContentMode.top
    self.cyanDogViewImage.clipsToBounds = true
    self.cyanDogViewImage.image = UIImage(named: “CyanDogImage”)
}

func updateImage() {
    cyanDogViewImage.frame.size.height = 
cyanDogViewImage.frame.size.height + 0.07

    if cyanDogViewImage.frame.size.height >= blackDogViewImage.frame.size.height
{
 timer.invalidate()
 cyanDogViewImage.frame.size.height = blackDogViewImage.frame.size.height
    }
}

func outOfTime() {
    timer.invalidate()
}

Upvotes: 0

McDonal_11
McDonal_11

Reputation: 4075

I hope you have done number 1 and number 2, perfectly. I have tried for number 3.

I have tried with two UIView. Its working fine. I thought, it will give some idea to achieve yours.

I have two images.

enter image description here

With the help of TIMER , I tried sample for this ProgressView .

Intially, cyanDogView height should be Zero. Once Timer Starts, height should increased by 2px. Once cyanDogView's height should be greater than BlackDogView, then Timer Stops.

Coding

@IBOutlet weak var blackDogView: UIView!
@IBOutlet weak var blackDogImgVw: UIImageView!

@IBOutlet weak var cyanDogView: UIView!
@IBOutlet weak var cyanDogImgVw: UIImageView!

var getHeight : CGFloat = 0.0
var progressTime = Timer()

override func viewDidAppear(_ animated: Bool) {

    cyanDogView.frame.size.height = 0

    getHeight = blackDogView.frame.height
}

@IBAction func startAnimateButAcn(_ sender: UIButton) {

    progressTime = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)

}


@objc func update() {
    cyanDogView.frame.size.height = cyanDogView.frame.size.height + 2

    if cyanDogView.frame.size.height >= getHeight
    {
        progressTime.invalidate()
        cyanDogView.frame.size.height = 0
    }
}

Story Board

enter image description here

Output

enter image description here

Upvotes: 1

Jake
Jake

Reputation: 2216

I'm going to give you some Frankenstein answers here, Part Obj-C Part Swift. I hope it helps.

First, You could create a bezier path of the image mask you're using as a template:

- (UIImage *)cerateImageFromImage:(UIImage *)image
                withMaskImage:(UIImage *)mask {
CGImageRef imageRef = image.CGImage;
CGImageRef maskRef = mask.CGImage;

CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                         CGImageGetHeight(maskRef),
                                         CGImageGetBitsPerComponent(maskRef),
                                         CGImageGetBitsPerPixel(maskRef),
                                         CGImageGetBytesPerRow(maskRef),
                                         CGImageGetDataProvider(maskRef),
                                         NULL,
                                         YES);

CGImageRef maskedReference = CGImageCreateWithMask(imageRef, imageMask);
CGImageRelease(imageMask);

UIImage *maskedImage = [UIImage imageWithCGImage:maskedReference];
CGImageRelease(maskedReference);

return maskedImage;
}

UIImage *image = [UIImage imageNamed:@"Photo.png"];
UIImage *mask = [UIImage imageNamed:@"Mask.png"];
self.imageView.image = [self cerateImageFromImage:image
                                     withMaskImage:mask];

Credit to Keenle

Next you can create a custom progress view based on a path :

func drawProgressLayer(){

   let bezierPath = UIBezierPath(roundedRect: viewProg.bounds, cornerRadius: viewCornerRadius)
   bezierPath.closePath()
   borderLayer.path = bezierPath.CGPath
   borderLayer.fillColor = UIColor.blackColor().CGColor
   borderLayer.strokeEnd = 0
   viewProg.layer.addSublayer(borderLayer)


}

//Make sure the value that you want in the function `rectProgress` that is going to define 
//the width of your progress bar must be in the range of
// 0 <--> viewProg.bounds.width - 10 , reason why to keep the layer inside the view with some border left spare.
//if you are receiving your progress values in 0.00 -- 1.00 range , just multiply your progress values to viewProg.bounds.width - 10 and send them as *incremented:* parameter in this func

func rectProgress(incremented : CGFloat){

    print(incremented)
    if incremented <= viewProg.bounds.width - 10{
        progressLayer.removeFromSuperlayer()
        let bezierPathProg = UIBezierPath(roundedRect: CGRectMake(5, 5, incremented , viewProg.bounds.height - 10) , cornerRadius: viewCornerRadius)
        bezierPathProg.closePath()
        progressLayer.path = bezierPathProg.CGPath
        progressLayer.fillColor = UIColor.whiteColor().CGColor
        borderLayer.addSublayer(progressLayer)

    }

  }

Credit to Dravidian

Please click the blue links and explore their answers in full to get a grasp of what is possible.

Upvotes: 0

Related Questions