Create a Loading Image/ Activity Indicator, Until the Image Is Shown in the Screen in Swift

create a loading image/ activity indicator, until the image is shown in the screen in swift

You got this problem, because:

  1. You didn't add the activityIndicator to any views
  2. You configured the activityIndicator in the completionHandler block

Let configure in the activityIndicator the viewDidLoad() method:

self.activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
self.activityIndicator.frame = CGRect(x: 0, y: 0, width: 46, height: 46)
self.activityIndicator.hidesWhenStopped = true

view.addSubview(self.activityIndicator)

And start to animate it before resuming the dataTask

self.activityIndicator.startAnimating()

Then, stop it in the completionHandler

self.activityIndicator.stopAnimating()

The final code:

let mygroup = DispatchGroup()
var activityIndicator = UIActivityIndicatorView()

override func viewDidLoad() {
super.viewDidLoad()

self.activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
self.activityIndicator.frame = CGRect(x: 0, y: 0, width: 46, height: 46)
self.activityIndicator.hidesWhenStopped = true

view.addSubview(self.activityIndicator)
}

func downloadpic(sender: UIButton) {
let catPictureURL = URL(string: addr)!

// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)

// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.

self.activityIndicator.startAnimating()

let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
// put loading screen here

self.mygroup.enter()
print("downloading image")
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
self.images.image = UIImage(data: imageData)
self.mygroup.leave()
print("image already downloaded")
self.activityIndicator.stopAnimating()

// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}

downloadPicTask.resume()
}

The result!

Sample Image

Activity indicator until load the next view

You should load the images in a background thread and display the UIActivityIndicator in the main thread. I already replied to a similar issue here: https://stackoverflow.com/a/41529056/1370336

// Main thread by default: 
// show progress bar here.

DispatchQueue.global(qos: .background).async {
// Background thread:
// start loading your images here

DispatchQueue.main.async {
// Main thread, called after the previous code:
// hide your progress bar here
}
}

I want to show activity indicator before image loading in ios

You could do it this way :

Add an indicator to your view, place it at the center of your imageview

    UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[indicator startAnimating];
[indicator setCenter:self.imageView.center];
[self.contentView addSubview:indicator];

Remove the indicator from the superview in the block's succes method.

     NSUrl *anURL=[NSURL URLWithString:[[NSString stringWithFormat:@"image url"]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding ]];

[_imageView setImageWithURL:[NSURL URLWithString:anURL]
success:^(UIImage *image) {
[indicator removeFromSuperview];
}
failure:^(NSError *error) {

}];
}

Of course you could make a nice subclass for this

Activity indicator in SwiftUI

As of Xcode 12 beta (iOS 14), a new view called ProgressView is available to developers, and that can display both determinate and indeterminate progress.

Its style defaults to CircularProgressViewStyle, which is exactly what we're looking for.

var body: some View {
VStack {
ProgressView()
// and if you want to be explicit / future-proof...
// .progressViewStyle(CircularProgressViewStyle())
}
}

Xcode 11.x

Quite a few views are not yet represented in SwiftUI, but it's easily to port them into the system.
You need to wrap UIActivityIndicator and make it UIViewRepresentable.

(More about this can be found in the excellent WWDC 2019 talk - Integrating SwiftUI)

struct ActivityIndicator: UIViewRepresentable {

@Binding var isAnimating: Bool
let style: UIActivityIndicatorView.Style

func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
return UIActivityIndicatorView(style: style)
}

func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
}
}

Then you can use it as follows - here's an example of a loading overlay.

Note: I prefer using ZStack, rather than overlay(:_), so I know exactly what's going on in my implementation.

struct LoadingView<Content>: View where Content: View {

@Binding var isShowing: Bool
var content: () -> Content

var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {

self.content()
.disabled(self.isShowing)
.blur(radius: self.isShowing ? 3 : 0)

VStack {
Text("Loading...")
ActivityIndicator(isAnimating: .constant(true), style: .large)
}
.frame(width: geometry.size.width / 2,
height: geometry.size.height / 5)
.background(Color.secondary.colorInvert())
.foregroundColor(Color.primary)
.cornerRadius(20)
.opacity(self.isShowing ? 1 : 0)

}
}
}

}

To test it, you can use this example code:

struct ContentView: View {

var body: some View {
LoadingView(isShowing: .constant(true)) {
NavigationView {
List(["1", "2", "3", "4", "5"], id: \.self) { row in
Text(row)
}.navigationBarTitle(Text("A List"), displayMode: .large)
}
}
}

}

Result:

Sample Image

Why don't loading indicator show when user save image to library in Swift?

Save image on background thread and do the UI handling on main thread

showLoading(viewCtrl: self)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in

// save image code here

dispatch_async(dispatch_get_main_queue(), { () -> Void in
hideLoading(viewCtrl: self)
showSuccess(viewCtrl: self)
})
});

Does Xcode have a built in loading animation for UIImageView?

There is no built in loading animation on UIImageView. You can extend UIImageView as below to add UIActivityIndicatorView,

extension UIImageView {

//// Returns activity indicator view centrally aligned inside the UIImageView
private var activityIndicator: UIActivityIndicatorView {
let activityIndicator = UIActivityIndicatorView()
activityIndicator.hidesWhenStopped = true
activityIndicator.color = UIColor.black
self.addSubview(activityIndicator)

activityIndicator.translatesAutoresizingMaskIntoConstraints = false

let centerX = NSLayoutConstraint(item: self,
attribute: .centerX,
relatedBy: .equal,
toItem: activityIndicator,
attribute: .centerX,
multiplier: 1,
constant: 0)
let centerY = NSLayoutConstraint(item: self,
attribute: .centerY,
relatedBy: .equal,
toItem: activityIndicator,
attribute: .centerY,
multiplier: 1,
constant: 0)
self.addConstraints([centerX, centerY])
return activityIndicator
}

/// Asynchronous downloading and setting the image from the provided urlString
func setImageFrom(_ urlString: String, completion: (() -> Void)? = nil) {
guard let url = URL(string: urlString) else { return }

let session = URLSession(configuration: .default)
let activityIndicator = self.activityIndicator

DispatchQueue.main.async {
activityIndicator.startAnimating()
}

let downloadImageTask = session.dataTask(with: url) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let imageData = data {
DispatchQueue.main.async {[weak self] in
var image = UIImage(data: imageData)
self?.image = nil
self?.image = image
image = nil
completion?()
}
}
}
DispatchQueue.main.async {
activityIndicator.stopAnimating()
activityIndicator.removeFromSuperview()
}
session.finishTasksAndInvalidate()
}
downloadImageTask.resume()
}
}

Custom image with rotation in activity indicator for iPhone application in swift

You can try something like this:

final class MyActivityIndicator: UIImageView {

override func startAnimating() {
isHidden = false
rotate()
}

override func stopAnimating() {
isHidden = true
removeRotation()
}

private func rotate() {
let rotation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.toValue = NSNumber(value: Double.pi * 2)
rotation.duration = 1
rotation.isCumulative = true
rotation.repeatCount = Float.greatestFiniteMagnitude
layer.add(rotation, forKey: "rotationAnimation")
}

private func removeRotation() {
layer.removeAnimation(forKey: "rotationAnimation")
}
}

Usage:

let activityIndicator = MyActivityIndicator(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
activityIndicator.image = UIImage(named: "TOU IMAGE")
view.addSubview(activityIndicator)
activityIndicator.startAnimating()


Related Topics



Leave a reply



Submit