Loading overlay not appearing while going to another viewcontroller - ios

My overlay public class is:
public class LoadingOverlay{
var overlayView = UIView()
var activityIndicator = UIActivityIndicatorView()
class var shared: LoadingOverlay {
struct Static {
static let instance: LoadingOverlay = LoadingOverlay()
}
return Static.instance
}
public func showOverlay(view: UIView) {
overlayView.frame = CGRectMake(0, 0, 80, 80)
overlayView.center = view.center
overlayView.backgroundColor = UIColor(white: 0.5, alpha: 0.7)
overlayView.clipsToBounds = true
overlayView.layer.cornerRadius = 10
activityIndicator.frame = CGRectMake(0, 0, 40, 40)
activityIndicator.activityIndicatorViewStyle = .WhiteLarge
activityIndicator.center = CGPointMake(overlayView.bounds.width / 2, overlayView.bounds.height / 2)
overlayView.addSubview(activityIndicator)
view.addSubview(overlayView)
activityIndicator.startAnimating()
}
public func hideOverlayView() {
activityIndicator.stopAnimating()
overlayView.removeFromSuperview()
}
}//end of class
This works fine but when I try to show this on click of UITableViewCell which pushes the user to next viewcontroller and next ViewController gets data from a database, the overlay is not displayed.
I want to display this when I click one of the cells in UITableView and then stop it when the viewdiddisappear or till the time the other view controller did appear. Because it is a rough 2-3 second gap sometimes until it loads. Also the same thing i would like to use while transitioning of tab bars to other controllers within tab bars (Those other controllers too get data from a database).

Related

Activity Indicator appeared in Wrong View Hierarchy

I have set up the activity indicator while loading to next view. The original view contains a tableView of buttons and it will direct to the next view when the button is clicked. However, it doesn't show up when coming back to the original view. It works on another view transition but I don't know why it doesn't work here.
Here's the button clicking function of processing to next view:
#objc func remark(_ sender: UIButton) {
ViewControllerUtils().showActivityIndicator(uiView: self.tableView)
DispatchQueue.main.async {
self.markDetailsDelegate?.getStudentList(paperId: sender.paperId!, schoolClass: sender.schoolClass!, studentToShow: sender.studentToShow, questionNo: sender.questionNo!, readOnlyFlag: false) { returnList, studentReturnList, returnMarkBox, returnMCOptionBox, returnStudentMarkList, error in
self.pdfFileList = returnList!
self.studentList = studentReturnList!
self.questionMarkBox = returnMarkBox!
self.mcQuestionOptionBox = returnMCOptionBox!
self.studentMarkList = returnStudentMarkList!
}
DispatchQueue.main.async {
ViewControllerUtils().hideActivityIndicator(uiView: self.tableView)
}
}
}
Function of reloading the tableView (once it's returning back to the tableView, it is called to reload it):
#objc func reloadMarkDetails() {
ViewControllerUtils().showActivityIndicator(uiView: self.tableView)
DispatchQueue.main.async {
self.markDetailsDelegate?.reloadMarkDetails(paperId: self.currentPaperId, schoolClass: self.currentSchoolClass) { returnPDFFileList, returnStudentList, returnQuestionMarkBox, returnMCQuestionOptionBox, returnStudentMarkList, returnPaperCommentList, returnPaperSummary, returnMarkSummary, returnTagSummary, returnLastMarkedSummary, returnAnsNoOfPage, returnDatetime, returnPaperFreezedFlag, error in
self.studentList = returnStudentList
self.studentMarkList = returnStudentMarkList
self.markSummary = returnMarkSummary
self.lastMarkedSummary = returnLastMarkedSummary
self.paperFreezedFlag = returnPaperFreezedFlag
DispatchQueue.main.async {
ViewControllerUtils().hideActivityIndicator(uiView: self.tableView)
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
}
}
}
}
Utility class of managing activity indicator:
class ViewControllerUtils: UIView {
static var shared = ViewControllerUtils()
var container: UIView = UIView()
var loadingView: UIView = UIView()
var activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
var loadingTextLabel = UILabel()
let margin: CGFloat = 20.0
/*
Show customized activity indicator (with text),
actually add activity indicator to passing view
#param uiView - add activity indicator to this view
#param title - add title to activity indicator
*/
func showActivityIndicator(uiView: UIView, title: String) {
container.frame = uiView.frame
container.center = uiView.center
container.backgroundColor = UIColorFromHex(rgbValue: 0x444444, alpha: 0.2)
loadingView.frame = CGRect(x: 0, y: 0, width: 230, height: 60)
loadingView.center = uiView.center
loadingView.backgroundColor = UIColorFromHex(rgbValue: 0xffffff, alpha: 1.0)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
loadingView.layer.shadowColor = UIColor.black.cgColor
loadingView.layer.shadowOpacity = 1
loadingView.layer.shadowOffset = .zero
loadingView.layer.shadowRadius = 10
activityIndicator.frame = CGRect(x: 0.0, y: 5.0, width: 40, height: 40);
activityIndicator.style = UIActivityIndicatorView.Style.gray
activityIndicator.center = CGPoint(x: (activityIndicator.frame.size.width + margin) / 2, y: loadingView.frame.size.height / 2)
loadingTextLabel.textColor = UIColor.black
loadingTextLabel.text = title
loadingTextLabel.font = .systemFont(ofSize: 16, weight: .medium)
loadingTextLabel.textAlignment = .center
loadingTextLabel.sizeToFit()
loadingTextLabel.adjustsFontSizeToFitWidth = true
loadingTextLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
loadingTextLabel.frame = CGRect(x: activityIndicator.frame.size.width + margin * 0.5, y: activityIndicator.frame.origin.y, width: loadingView.frame.size.width - activityIndicator.frame.size.width - margin, height: activityIndicator.frame.size.height)
loadingView.addSubview(activityIndicator)
loadingView.addSubview(loadingTextLabel)
container.addSubview(loadingView)
uiView.superview?.addSubview(container)
activityIndicator.startAnimating()
}
/*
Show customized activity indicator (without text),
actually add activity indicator to passing view
#param uiView - add activity indicator to this view
*/
func showActivityIndicator(uiView: UIView) {
container.frame = uiView.frame
container.center = uiView.center
container.backgroundColor = UIColorFromHex(rgbValue: 0xffffff, alpha: 0.5)
loadingView.frame = CGRect(x: 0, y: 0, width: 80, height: 80)
loadingView.center = container.center
loadingView.backgroundColor = UIColorFromHex(rgbValue: 0x444444, alpha: 0.7)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
loadingView.layer.cornerRadius = 10
loadingView.layer.shadowColor = UIColor.black.cgColor
loadingView.layer.shadowOpacity = 1
loadingView.layer.shadowOffset = .zero
loadingView.layer.shadowRadius = 10
loadingTextLabel.removeFromSuperview()
activityIndicator.frame = CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0);
activityIndicator.style = UIActivityIndicatorView.Style.whiteLarge
activityIndicator.center = CGPoint(x: loadingView.frame.size.width / 2, y: loadingView.frame.size.height / 2);
loadingView.addSubview(activityIndicator)
container.addSubview(loadingView)
uiView.superview?.addSubview(container)
activityIndicator.startAnimating()
}
/*
Change text of activity indicator
#param message - modified text
*/
func changeTextToActivityIndicator(message: String) {
loadingTextLabel.text = message
}
/*
Hide activity indicator
Actually remove activity indicator from its super view
#param uiView - remove activity indicator from this view
*/
func hideActivityIndicator(uiView: UIView) {
activityIndicator.stopAnimating()
container.removeFromSuperview()
print("[DEBUG] Stop Activity Indicator at View - \(uiView)")
}
/*
Define UIColor from hex value
#param rgbValue - hex color value
#param alpha - transparency level
*/
func UIColorFromHex(rgbValue:UInt32, alpha:Double=1.0)->UIColor {
let red = CGFloat((rgbValue & 0xFF0000) >> 16)/256.0
let green = CGFloat((rgbValue & 0xFF00) >> 8)/256.0
let blue = CGFloat(rgbValue & 0xFF)/256.0
return UIColor(red:red, green:green, blue:blue, alpha:CGFloat(alpha))
}
}
I tried to figure out why the indicator is not showing while returning back to original view by using Debug View Hierarchy and here are the results:
View A processing to next View B (no problem)
View B returning back to original View A (activity indicator goes to wrong view hierarchy)
Is there anything I did wrong during segue transition? Thanks so much if someone could help me!
I assume it should be
#objc func remark(_ sender: UIButton) {
ViewControllerUtils().showActivityIndicator(uiView: self.tableView)
self.markDetailsDelegate?.getStudentList(paperId: sender.paperId!, schoolClass: sender.schoolClass!, studentToShow: sender.studentToShow, questionNo: sender.questionNo!, readOnlyFlag: false) { returnList, studentReturnList, returnMarkBox, returnMCOptionBox, returnStudentMarkList, error in
DispatchQueue.main.async {
ViewControllerUtils().hideActivityIndicator(uiView: self.tableView)
self.pdfFileList = returnList!
self.studentList = studentReturnList!
self.questionMarkBox = returnMarkBox!
self.mcQuestionOptionBox = returnMCOptionBox!
self.studentMarkList = returnStudentMarkList!
}
}
}

Move UIView from ViewController to Window

Thanks for taking the time to read thus. So basically, I have a UIView in my UIViewController. I want a user to be able to press a button and then the UIView moves from my UIViewController to the my application's window so that the UIView will be above all UIViewControllers. The only thing I could think of doing was
class ViewController: UIViewController {
var window = UIApplication.shared.keyWindow!
var view = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(view)
}
func tappedAction() {
window.bringSubview(toFront: view)
}
}
but that didn't work. Does anyone have any ideas on how to accomplish this?
You cannot just bring the subview that's in your UIViewController to the front of your UIWindow.
You need to:
Remove the UIView from the UIViewController.
Add the UIView to the main UIWindow.
I chose to do this in this way:
import UIKit
class ViewController: UIViewController {
var customView: UIView!
// Load the main view of the UIViewController.
override func loadView() {
view = UIView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Load the custom view that we will be transferring.
self.customView = UIView(frame: .init(x: 100, y: 250, width: 250, height: 250))
self.customView.backgroundColor = .red
view.addSubview(customView)
// Transfer the view. Call this method in your trigger function.
transfer(self.customView)
}
func transfer(_ view: UIView) {
// Remove the view from the UIViewController.
view.removeFromSuperview()
// Add the view to the UIWindow.
UIApplication.shared.windows.first!.addSubview(view)
}
}
You must set frame fro view at var view = UIView()
then you should add to window window.addSubview(view)
If your view is added on window then window.bringSubview(toFront: view) will work otherwise it will not.
If your view is added on window then you can use bringSubview(toFront:) like that:
Example:
let window = UIApplication.shared.keyWindow!
let view1 = UIView(frame: CGRect(x: window.frame.origin.x, y: window.frame.origin.y, width: window.frame.width, height: window.frame.height))
window.addSubview(view1);
view1.backgroundColor = UIColor.black
let view2 = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
view2.backgroundColor = UIColor.white
window.addSubview(view2)
UIApplication.shared.keyWindow!.bringSubview(toFront: view1)
So you need to add your view in window:
window.addSubview(view)

Calling a function (containing variable) in another class

I have a relatively simple question. I created a function - covers the contents to retrieve data from the API. I need to call a function in several different classes (don't want to duplicate). I am calling that function, but nothing is getting displayed. If this function is a part of class where I want to call it, it works correctly. Where is the problem, please let me know. (I think in UIView...)? Thank you.
My function:
func loadScreen() {
var loadView:UIView!
loadView = UIView(frame: self.view.frame)
loadView!.backgroundColor = UIColor.black
loadView!.alpha = 1.0
let actView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
actView.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
actView.center = CGPoint(x: self.view.bounds.size.width / 2 , y: self.view.bounds.size.height / 2 )
actView.startAnimating()
loadView?.addSubview(actView)
self.view.addSubview(loadView!)
}
I call a function in another class in viewDidLoad (), but as I said, nothing is displayed.
First create a singleton class like this
Singleton class
import UIKit
import Foundation
class Singleton: NSObject
{
static let sharedInstance = Singleton()
override init()
{
super.init()
}
func placeBlockerView(target : UIViewController)
{
var loadView:UIView!
loadView = UIView(frame: target.view.frame)
loadView!.backgroundColor = UIColor.black
loadView!.alpha = 1.0
let actView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
actView.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
actView.center = CGPoint(x: target.view.bounds.size.width / 2 , y: target.view.bounds.size.height / 2 )
actView.startAnimating()
loadView?.addSubview(actView)
target.view.addSubview(loadView!)
}
}
After that use it from where ever you want as required
like this
Singleton.sharedInstance.placeBlockerView(target: self)
This above line will add that UIView you have created to your view controller's view.
Let me know if it don't work.
Instead of adding it in the above, it is always advised to return the UIView from that method and in your subclass of UIViewController do the following.
func viewDidLoad() {
let loadView = loadScreen()
self.view.addSubView()
}

How to Add UIToolbar with two button in UIPickerView(swift 2.0)?

I would like to add two UIButtons in the UIPickerView (on bottom of it). Please take a look at the Cancel and Done buttons in this image:
Here My code Upload:-
class DatePicker{
var containerView = UIView()
var datePicker = UIView()
var datePickerView = UIDatePicker()
var toolBar = UIToolbar()
internal class var shared: DatePicker {
struct Static {
static let instance: DatePicker = DatePicker()
}
return Static.instance
}
internal func showProgressView(view: UIView) {
containerView.frame = view.frame
containerView.center = view.center
containerView.backgroundColor = UIColor(hex: 0xffffff, alpha: 0.3)
datePickerView.datePickerMode = .Date
datePicker.frame = CGRectMake(0, 0, 250, 250)
datePicker.center = view.center
datePicker.backgroundColor = UIColor(hex: 0x444444, alpha: 0.7)
datePicker.clipsToBounds = true
datePicker.layer.cornerRadius = 10
datePickerView.setValue(UIColor.whiteColor(), forKeyPath: "textColor")
datePickerView.frame = CGRectMake(0, 0, 250, 250)
datePicker.addSubview(datePickerView)
containerView.addSubview(datePicker)
view.addSubview(containerView)
}
internal func hideProgressView() {
containerView.removeFromSuperview()
}
}
How can i get UIToolbar And Two Button?
I would recommend making a .xib with the toolbar and picker view in it. Put two bar buttons on the bar and put a bar button spacer in between them.
Link on how to use a xib
You can make the animation of it appearing look great if you start its frame off screen and use this function to move it on screen.
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
if viewToMove != nil {
viewToMove!.frame.origin.y/*orX*/ = onScreenPosition
} else {
"sidePanel == nil"
}
}, completion: completion)
usingSpringWithDamping can be set to 1 if you don't want any bounce in animation. If this is for an iPhone, i would recommend making the initial frame equal to something like
CGRectMake(0, UIScreen.mainScreen().bounds.height, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height/2)
and then the view to move line would become
viewToMove!.frame.origin.y = UIScreen.mainScreen().bounds.height/2
using the previous function to bring it on screen and
viewToMove!.frame.origin.y = UIScreen.mainScreen().bounds.height
to move it off screen. You can access the buttons of the xib in a few different ways, but I recommend using a delegate.
Here is a guide to them.
If you are not familiar with them, I would become familiar, because they are very useful! If you have any questions don't hesitate to ask!

UIActivityIndicatorView dialog over view

In my app I'm using a custom UIActivityIndicator view (this) and it works great.
I have an UITableView and I want the indicator over the tableview on loading data. To solve the problem I have created an empty view with a background above all and when I need to start indicator's animation I simply hide tableview and show the empty view + the indicator.
The result is this:
I saw somewhere that I can use instead something like this:
How can I do?
Thanks in advance.
You could try adding a translucent Black view over the Table View and put the custom activity indicator code as a subview in the Black view.
let aBlackView = UIView(frame: CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height))
aBlackView.backgroundColor = UIColor.blackColor()
aBlackView.alpha = 0.75 // Change the alpha depending on how much transparency you require
let aMainWindow = UIApplication.sharedApplication().delegate!.window
aMainWindow!!.addSubview(aBlackView)
/* Enter you code for NVActivityIndicatorViewable here */
aBlackView.addSubview(/* NVActivityIndicatorViewable */)
Once you are done with your request, you can remove the Black view by calling this code:
aBlackView.removeFromSuperview()
I created a activity showing screen . I used in every where in my project.
//ProgressBarNotification.swift
import Foundation
import UIKit
class ProgressBarNotification {
internal static var strLabel = UILabel()
internal static var messageFrame = UIView()
internal static var activityIndicator = UIActivityIndicatorView()
internal static func progressBarDisplayer(msg:String,indicator:Bool ){
let view1 = UIApplication.sharedApplication().delegate?.window!!.rootViewController?.view
view1?.userInteractionEnabled = false
strLabel = UILabel(frame: CGRect(x: 50, y: 0, width: 200, height: 50))
strLabel.text = msg
strLabel.textColor = UIColor.whiteColor()
messageFrame = UIView(frame: CGRect(x: view1!.frame.midX - 90, y: view1!.frame.midY - 25 , width: 180, height: 50))
messageFrame.layer.cornerRadius = 15
messageFrame.backgroundColor = UIColor(white: 0, alpha: 0.7)
if indicator {
activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
activityIndicator.startAnimating()
messageFrame.addSubview(activityIndicator)
}
messageFrame.addSubview(strLabel)
view1!.addSubview(messageFrame)
}
internal static func removeProgressBar() {
let view1 = UIApplication.sharedApplication().delegate?.window!!.rootViewController?.view
_ = view1?.subviews.filter({ (view) -> Bool in
if view == ProgressBarNotification.messageFrame {
view.removeFromSuperview()
}
return false
})
ProgressBarNotification.messageFrame.removeFromSuperview()
view1?.userInteractionEnabled = true
}
deinit{
}
}
Usage
//To start Loader
ProgressBarNotification.progressBarDisplayer("Loading", indicator: true)
//Stop
ProgressBarNotification.removeProgressBar()

Resources