create a loading image/ activity indicator, until the image is shown in the screen in swift
You got this problem, because:
- You didn't add the
activityIndicator
to any views - You configured the
activityIndicator
in thecompletionHandler
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!
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:
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
Format Println Output in a Table
"Raw Value for Enum Case Is Not Unique" for Swift Enum with Float Raw Values
How to Set the iOS13 Uisegmentedcontrol Backgroundcolor to White
How to Switch an Xcode Project to Use Swift Version 1.2 in the Xcode 7 Beta
Generic and (Early) Binding in Swift 1.2
How to Set Alignment for Wkinterface Label Using Setattributedtext
Can Somebody Post a Good Example of MVC Pattern in Swift
How to Set iOS 13 Glyphs Programmatically
How to Cancel Firebase Setvalue While Pending for Completion (When Offline)
Swift Unit Testing with Xctassertthrows Analogue
How to Set Priority on Constraints in Swift
Toolbar Is Deleting My Back Button in the Navigationview
Xcode Playgrounds Shared Directory Not Working
Classes in Swift Files Inside Folder References Not Seen by Xcode 10's Compiler
Swift Combine: What Are Those Multicast Functions for and How to Use Them