I'm currently attempting to make a global activity indicator in swift, which is called whenever a fetch to the api is made. The idea is that the activity indicator will appear in the top left of a navigation bar (navigation view controller's child), and is available on every app page.
Can't show example image due to new account / low rep
I have the activity indicator displaying correctly, I'm just not sure on how to make it available from any page on the app - i've considered an extension, but am not sure on what the best way to approach it is. Any help would be greatly appreciated.
Activity Indicator Code:
let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
var activityItem = UIBarButtonItem()
func navBarActivity() {
// Call navBarActivity() to start activity indicator
// Use navigationItem.leftBarButtonItem = nil to stop activity indicator
activityIndicator.startAnimating()
activityIndicator.hidden = false
self.activityItem = UIBarButtonItem(customView: activityIndicator)
self.navigationItem.leftBarButtonItem = activityItem
}
I would recommend extension like this.
extension UIViewController {
func displayNavBarActivity() {
let indicator = UIActivityIndicatorView(activityIndicatorStyle: .White)
indicator.startAnimating()
let item = UIBarButtonItem(customView: indicator)
self.navigationItem.leftBarButtonItem = item
}
func dismissNavBarActivity() {
self.navigationItem.leftBarButtonItem = nil
}
}
Call self.displayNavBarActivity() before api call, and call self.dismissNavBarActivity() after api call done.
However, I want you to check networkActivityIndicatorVisible of UIApplication. Consider using this option.
Specify YES if the app should show network activity and NO if it should not. The default value is NO. A spinning indicator in the status bar shows network activity. The app may explicitly hide or show this indicator.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instp/UIApplication/networkActivityIndicatorVisible
Related
I meet a question. I am using following code to display UIActivityIndicatorView. My requirement is to be able to create an UIActivityIndicatorView and display it when I click the button with tag 1, if I click other buttons the UIActivityIndicatorView will be removed from the super view.
class ViewController: UIViewController {
lazy var acLoad:(UIActivityIndicatorView) = {
let myActivityIndicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.white)
myActivityIndicator.center = view.center
myActivityIndicator.hidesWhenStopped = true
myActivityIndicator.startAnimating()
return myActivityIndicator
}()
//more code
#objc func btnAction(sender: UIButton){
switch sender.tag {
case 1:
print("created")
view.addSubview(acLoad)
acLoad.startAnimating()
default:
print("removed")
acLoad.stopAnimating()
acLoad.removeFromSuperview()
}
}
}
The above doesn't work, I can get the print log after click, but UIActivityIndicatorView doesn't display, any ideas?
It seems like your UIActivityIndicatorView might be misplaced (wrong frame value) - so it is not visible to you.
print("created")
view.addSubview(acLoad)
acLoad.startAnimating()
// Add this line in your button tap code
// It will tell you where it is placed inside your view
print(acLoad.frame)
If you find that it's frame is not where you want it to be, just fix your layout code and it will be where you expect it to be.
Another note - myActivityIndicator.hidesWhenStopped = true makes it automatically hide on stopAnimating() call. So you can add it only once, not remove-add every time.
Also check your view.backgroundColor vs acLoad style.
Below Gif is from my app, the 1st VC includes a search bar to filter the songs, and when press a row to transition to 2nd VC to show selected playing song.
The question here is that when 2nd VC is opened, the search bar is not disappeared immediately, it has like 1 or 2 seconds delay, could see that behavior from below GIF.
/ / / Here is my code, how could I solve this issue? Any hint is appreciate.
The search bar
var resultSearchController = UISearchController()
override func viewDidLoad() {
...
// add search bar
resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.hidesNavigationBarDuringPresentation = false
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
// set search Bar covered color, same with tableView's background color.
controller.searchBar.barTintColor = UIColor(rgb: 0x292f33)
self.tableView.tableHeaderView = controller.searchBar
return controller
})() // closure, learn it later!!
...
}
I set search bar to disabled state when leaving current VC.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
resultSearchController.isActive = false
}
/ / / Update, as matt's comment, I change the code to integrate search bar into navigation bar, and now the search bar is disappear immediately after opening VC2.
remove self.tableView.tableHeaderView = controller.searchBar and integrate the search bar into the nav bar navigationItem.searchController = resultSearchController. Now the behavior is same as Apple's inbox app.
searchController.searchBar.isHidden = false
Hiding the searchController instead of making the active state to false may solve your issue.
Try adding in the above line to your code in viewWillDisappear()
hopefully this helps.
i have a UICollectionViewController where i am showing list of task which is working fine, recently i tried to implement a UISearchBar for my TaskController after implementing that, when i try to launch any new viewcontroller by clicking on row inside my TaskController the newly launched view controller does not have UINavigationBar so i cant move back to my task list again. see following TaskController with task list:
Image
in above screen shot there is a star icon when user click on that, I launch following view controller which have a navigation bar(note: I have click directly without filtering records thats why i can see the navigation bar here.). UIViewController with UINavigationBar
Image
this is what i get when i click on star icon after filtering data with search bar.
navigation bar gone missing here
so i can not go back to task list controller also when i change a tab from below and come back the view controller got destroyed and i get a black screen with tab bar.
following code i have used to implement search bar which have the problem please help me to figure it out.
let taskSearchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
//set taskSearchController
taskSearchController.searchResultsUpdater = self
taskSearchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = taskSearchController
getTaskList(){
}
}
following method gives the filtered data from tasklist
func updateSearchResults(for searchController: UISearchController) {
guard let searchText = searchController.searchBar.text, !searchText.isEmpty else{
self.taskList = self.originalTaskist
collectionView?.reloadData()
return
}
taskList = originalTaskist.filter({ task -> Bool in
return task.name!.lowercased().contains(searchText.lowercased())
})
collectionView?.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// This prevents the search bar to make trouble on pushed view controllers
definesPresentationContext = true
//...
}
Put definesPresentationContext = true inside of your View Controller that shows the search bar (the UICollectionViewController in your case.
Unfortunately, the documentation doesn't explain very well why this is working. This blog post explains it a little better.
so, i'm using swift library ImageSlideshow and use KingfisherSource to set image. I get the url image from Alamofire request
How to make loading/activity indicator before image showed?
Here's my code:
var arrayImage = [InputSource]()
for data in items{
let Menu = ModelBanner()
Menu.id = (data["id"].intValue)
Menu.banner = (data["banner"].stringValue)
self.listBanner.append(Menu)
self.arrayImage.append(KingfisherSource(urlString: data["banner"].stringValue)!)
}
self.slideImage.contentScaleMode = .scaleAspectFill
self.slideImage.setImageInputs(self.arrayImage)
You can add activity indicator from the storyboard over your UIImageView.
Connect an outlet from the activity indicator to your ViewController file
In viewDidLoad(), activityIndicator.hidesWhenStopped = true
Within your Alamofire request, use activityIndicator.startAnimating()
Once you get the call back from Alamofire, in the completion handler, use activityIndicator.stopAnimating()
Above code assumes activityIndicator is the outlet connection name of the UIActivityIndicatorView
Better way to show activity indicator is to use the default implementation.
slideshow.activityIndicator = DefaultActivityIndicator()
You can customize style and color of the indicator:
slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil)
There's also an option to use your own activity indicator. You just need to implement ActivityIndicatorView and ActivityIndicatorFactory protocols.
For more details see the Activity Indicator section here.
I am developing an app, where I create buttons programmatically. When I click a button, it will request data from a database and show it in another view.
I use button.tag to determine what data to request, and I can only get the tag once the button is clicked.
However when I click the button, it shows nothing the first time. I must click it again to see the data which I want.
override func viewDidLoad() {
super.viewDidLoad()
//parseJSON(tagId)
createButton()
// Do any additional setup after loading the view, typically from a nib.
}
func createButton(){
var j:CGFloat=60
for var i:Int = 0 ; i < myImages.count;i = i+1 {
let myButton = UIButton()
myButton.setImage(UIImage(named: "carasusto.jpg"), forState: UIControlState.Normal)
myButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
myButton.frame = CGRectMake(j, j+60, 50, 50)
myButton.tag = i //assign a tag to every button
myButton.addTarget(self, action: "segueToCreate:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(myButton)
j=j+60
print(myImages[i])
}
}
and
#IBAction func segueToCreate(sender: UIButton){
tagId = String(sender.tag)//tagId needs to fetch the information
parseJSON(tagId)
performSegueWithIdentifier("segueToView", sender:self)
}
func parseJSON(tagID:String){
Alamofire.request(.GET, "http://smarttags-001-site1.btempurl.com/SmartTagsRequests.aspx", parameters: ["AjaxFunc":"GetTagAttr","TagID":"\(tagID)"]).validate().responseJSON{ response in
switch response.result{
case .Success:
if let value = response.result.value {
let json = JSON(value)
print("JSON: \(json)")
self.TagName = json[0]["TagName"].stringValue
NSLog("\(self.TagName)")
self.ContentTitle = json[0]["ContentTitle"].stringValue
NSLog("\(self.ContentTitle)")
}
case .Failure(let error):
print(error)
}enter code here
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var ViewTest : ViewTwo = segue.destinationViewController as! ViewTwo
var TagNameLabel = UILabel()
TagNameLabel.frame = CGRectMake(74, 112, 182, 64)
ViewTest.view.addSubview(TagNameLabel)
TagNameLabel.text = TagName
var ContentTitleLabel = UILabel()
ContentTitleLabel.frame = CGRectMake(74, 180, 182, 64)
ViewTest.view.addSubview(ContentTitleLabel)
ContentTitleLabel.text = ContentTitle
}
}
To follow up to MirekE's answer, here are some other steps you may want to consider:
Consider using Auto Layout in place of hard-coded frames, so your UI would adapt to different size classes.
Consider alternate approaches for showing an interactive list (of images) instead of programmatically adding buttons. For example, you could use a prototype (table view or collection view) cell. Cells are selectable and can take the place of a button. Other benefits include:
A scrollable container, should you have more buttons than would fit on screen.
A single segue (or selection) is handled by the storyboard prototype cell, instead of needing to make each programmatic "button" perform a segue (or action). Since you'd know which cell was selected, you'd no longer need a tag to figure that out.
Consider passing parameters to your destination view controller, instead of trying to instantiate and create controls in prepareForSegue. The destination's view is not loaded at that point.
Consider allowing the UI to feel more responsive, such as by performing the segue and showing placeholder details which you can then update once the network request completes. Otherwise, the user may have to wait for the network response before the segue occurs (which might make them think nothing happened and needlessly tap again, leading to an additional network request).
In general, Apple (or someone else) has already provided some way to do what you want, ideally leading to less code that you have to write, debug, test, and maintain to accomplish what you want your app to do.
Most likely cause of the problem is your call to parseJson followed by prepareForSegue. parseJson is asynchronous and you don't get data back from your service before prepareForSegue is called. As a first step, move the prepareForSegue to the completion block of the parseJson.