I'm trying to add UIActivityIndicatorView to UIAlertView but couldn't get it done. I have seen posts on this implementation and found out that it works only for versions below iOS7.
Below is the code I've tried...
var alert: UIAlertView = UIAlertView(title: "Title", message: "Please wait...", delegate: nil, cancelButtonTitle: "Cancel");
var loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(0.0, 0.0, 10.0, 10.0)) as UIActivityIndicatorView
loadingIndicator.center = self.view.center;
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();
alert.addSubview(loadingIndicator);
alert.show();
Is there anything specific thing to do for iOS7 and above to get this done?
try this code!!
var alert: UIAlertView = UIAlertView(title: "Title", message: "Please wait...", delegate: nil, cancelButtonTitle: "Cancel")
var loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRect(x: 50, y: 10, width: 37, height: 37)) as UIActivityIndicatorView
loadingIndicator.center = self.view.center
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
loadingIndicator.startAnimating();
alert.setValue(loadingIndicator, forKey: "accessoryView")
loadingIndicator.startAnimating()
alert.show();
I hope I helped you
For swift 4
Create an alert controller extension
extension UIAlertController {
private struct ActivityIndicatorData {
static var activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
}
func addActivityIndicator() {
let vc = UIViewController()
vc.preferredContentSize = CGSize(width: 40,height: 40)
ActivityIndicatorData.activityIndicator.color = UIColor.blue
ActivityIndicatorData.activityIndicator.startAnimating()
vc.view.addSubview(ActivityIndicatorData.activityIndicator)
self.setValue(vc, forKey: "contentViewController")
}
func dismissActivityIndicator() {
ActivityIndicatorData.activityIndicator.stopAnimating()
self.dismiss(animated: false)
}
}
Create Var a ctivityIndicatorAlert in your view controller and write display and dismiss methods in view controller like below
var activityIndicatorAlert: UIAlertController?
func displayActivityIndicatorAlert() {
activityIndicatorAlert = UIAlertController(title: NSLocalizedString("Loading", comment: ""), message: NSLocalizedString("PleaseWait", comment: "") + "...", preferredStyle: UIAlertController.Style.alert)
activityIndicatorAlert!.addActivityIndicator()
var topController:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while ((topController.presentedViewController) != nil) {
topController = topController.presentedViewController!
}
topController.present(activityIndicatorAlert!, animated:true, completion:nil)
}
func dismissActivityIndicatorAlert() {
activityIndicatorAlert!.dismissActivityIndicator()
activityIndicatorAlert = nil
}
You can also add UIViewcontroller extension For globally available above all methods.
extension UIViewController {
private struct activityAlert {
static var activityIndicatorAlert: UIAlertController?
}
//completion : ((Int, String) -> Void)?)
func displayIPActivityAlert(_ onCancel : (()-> Void)?) {
activityAlert.activityIndicatorAlert = UIAlertController(title: NSLocalizedString("Loading...", comment: ""), message: nil , preferredStyle: UIAlertController.Style.alert)
activityAlert.activityIndicatorAlert!.addActivityIndicator()
var topController:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while ((topController.presentedViewController) != nil) {
topController = topController.presentedViewController!
}
activityAlert.activityIndicatorAlert!.addAction(UIAlertAction.init(title:NSLocalizedString("Cancel", comment: ""), style: .default, handler: { (UIAlertAction) in
self.dismissIPActivityAlert()
onCancel?()
}))
topController.present(activityAlert.activityIndicatorAlert!, animated:true, completion:nil)
}
func dismissIPActivityAlert() {
activityAlert.activityIndicatorAlert!.dismissActivityIndicator()
activityAlert.activityIndicatorAlert = nil
}
}
For those using Objective-C. My solution wraps the UIActivityIndicatorView with a bigger view so the margin to the edge of UIAlertView is not so small.
#import "UIAlertView+ActivityIndicator.h"
#implementation UIAlertView (ActivityIndicator)
+ (UIAlertView *)alertViewWithActivityIndicator:(NSString *)title delegate:(id<UIAlertViewDelegate>)delegate;
{
UIAlertView *a = [[UIAlertView alloc] initWithTitle:title
message:nil
delegate:delegate
cancelButtonTitle:nil
otherButtonTitles:nil];
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
UIActivityIndicatorView *aiv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
aiv.translatesAutoresizingMaskIntoConstraints = NO;
[v addSubview:aiv];
[v addConstraint:[NSLayoutConstraint constraintWithItem:aiv
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:v
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0]];
[v addConstraint:[NSLayoutConstraint constraintWithItem:aiv
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:v
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0]];
[aiv startAnimating];
[a setValue:v forKey:#"accessoryView"];
return a;
}
#end
The accepted answer in Swift 3:
var alert: UIAlertView = UIAlertView(title: "Title", message: "Please wait...", delegate: nil, cancelButtonTitle: "Cancel");
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x:50, y:10, width:37, height:37))
loadingIndicator.center = self.view.center;
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
alert.setValue(loadingIndicator, forKey: "accessoryView")
loadingIndicator.startAnimating()
alert.show();
Related
Screenshot of the gap
I need to present a popover view from a UIBarbuttonItem in a UIToolbar. But there exists a gap between the popover view and the toolbar.
private func addToolbar() {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 50))
let item1 = UIBarButtonItem(image: UIImage(systemName: "pencil"), style: .plain, target: self, action: #selector(item1Pressed(_:)))
let item2 = UIBarButtonItem(image: UIImage(systemName: "house"), style: .plain, target: self, action: #selector(item2Pressed(_:)))
toolBar.sizeToFit()
toolBar.items = [item1, item2]
textField.inputAccessoryView = toolBar
}
class SearchViewController: UIViewController {
let searchBar = UISearchBar()
weak var viewControllerDelegate: SearchViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setupSearchbar()
}
private func setupView() {
view.backgroundColor = .white
navigationController?.navigationBar.prefersLargeTitles = true
self.title = "Search"
}
private func setupSearchbar() {
searchBar.sizeToFit()
searchBar.placeholder = "Search test"
searchBar.showsCancelButton = true
searchBar.delegate = self
self.navigationItem.titleView = searchBar
}
}
I tried setting the sourceRect of the presented popover view, but the popover view doesn't move down below a certain point.
#objc private func item1Pressed(_ sender: UIBarButtonItem) {
let vc = SearchViewController()
vc.viewControllerDelegate = self
let navVC = UINavigationController(rootViewController: vc)
navVC.modalPresentationStyle = .popover
navVC.popoverPresentationController?.delegate = self
navVC.popoverPresentationController?.permittedArrowDirections = .any
navVC.preferredContentSize = CGSize(width: 500, height: 200)
navVC.popoverPresentationController?.sourceItem = sender
var location = CGPoint(x: 0, y: 0)
if let barItemView = sender.value(forKey: "view") as? UIView {
let barFrame = barItemView.frame
let rect = barItemView.convert(barFrame, to: view)
location = rect.origin
}
navVC.popoverPresentationController?.sourceRect = CGRect(x: location.x, y: location.y+100, width: 0, height: 0)
present(navVC, animated: true)
}
I want to show a popover on iPad as soon as my view loads having as source a button on the top right corner.
The popover displays properly on button tap, but I'm having trouble finding a way to display it without the button tap, when the page first loads. Is this possible?
This is what I have:
func displayOptions(sourceButton: UIBarButtonItem)
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let popoverController = actionSheet.popoverPresentationController {
var rect = CGRect(x: 0, y: 0, width: 0, height: 0)
popoverController.barButtonItem = sourceButton
popoverController.sourceRect = rect
popoverController.permittedArrowDirections = .up
}
viewController.present(actionSheet, animated: true, completion: nil)
}
This code works properly on button tap, but the app crashes if I try to call this function from viewDidLoad or viewDidAppear:
You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem.
Try using UIPopoverPresentationControllerDelegate
Code example:
viewDidLoad
let buttonCustom = UIBarButtonItem(title: "Custom", style: .done, target: self, action: #selector(ViewController.customBarButtonAction))
self.navigationItem.rightBarButtonItem = buttonCustom
displayOptions()
displayOptions
func displayOptions() {
let actionSheet = UIAlertController(title: "Title", message: "Message", preferredStyle: .actionSheet)
if let popoverController = actionSheet.popoverPresentationController {
let rect = CGRect(x: 0, y: 0, width: 0, height: 0)
popoverController.sourceRect = rect
popoverController.sourceView = self.view
popoverController.permittedArrowDirections = .up
popoverController.delegate = self
}
self.present(actionSheet, animated: true, completion: nil)
}
using UIPopoverPresentationControllerDelegate
func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
popoverPresentationController.barButtonItem = self.navigationItem.rightBarButtonItem
}
You need to provide the sourceView.
UIAlertController must have a title, a message or an action to display.
// Create a bar button item in ViewDidLoad.
let button1 = UIBarButtonItem(image: UIImage(named: "imagename"), style: .plain, target: self, action: Selector("action"))
self.navigationItem.rightBarButtonItem = button1
// Call in ViewDidLoad
displayOptions(sourceButton: button1)
func displayOptions(sourceButton: UIBarButtonItem) {
let actionSheet = UIAlertController(title: "title", message: "message", preferredStyle: .actionSheet)
if let popoverController = actionSheet.popoverPresentationController {
let rect = CGRect(x: 0, y: 0, width: 0, height: 0)
popoverController.barButtonItem = sourceButton
popoverController.sourceRect = rect
popoverController.sourceView = self.view
popoverController.permittedArrowDirections = .up
}
self.present(actionSheet, animated: true, completion: nil)
}
normally view.endEditing(true) always removes the keyboard in swift on iOS.
however this is not always true, like when the UITextField is part of a self.present()'ed modal view i.e. a form inside a UIAlertController
Example
let alert = UIAlertController(title: _title, message: "Choose", preferredStyle: .alert)
let numberLabel = UILabel(frame: CGRect(x:10, y:0, width:90, height:20))
numberLabel.textColor = .black
numberLabel.textAlignment = NSTextAlignment.left
numberLabel.font = UIFont(name: titleLabel.font.fontName, size: 16)
numberLabel.font = UIFont.boldSystemFont(ofSize: titleLabel.font.pointSize)
numberLabel.text = "Quantity"
let quantityField = UITextField(frame: CGRect(x:100, y:0, width:140, height:25))
quantityField.layer.borderWidth = 1
quantityField.keyboardType = .numberPad
quantityField.layer.borderColor = UIColor.lightGray.cgColor
quantityField.delegate = self
self.addDoneButtonOnKeyboard(textField: quantityField)
self.present(alert, animated: true, completion: nil)
now in this self.addDoneButtonOnKeyboard method I have this nifty code
func addDoneButtonOnKeyboard(textField: UITextField){
let doneToolbar: UIToolbar = UIToolbar(frame: CGRect(x:0, y:0, width:320, height:50))
doneToolbar.barStyle = UIBarStyle.default
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(ViewControllerDisplay.doneButtonAction(_:)))
let items = NSMutableArray()
items.add(flexSpace)
items.add(done)
doneToolbar.items = items as? [UIBarButtonItem]
doneToolbar.sizeToFit()
textField.inputAccessoryView = doneToolbar
}
#objc func doneButtonAction(_ btn: UIBarButtonItem){
view.endEditing(true)
}
the purpose of all this is so that a "Done" keyboard button appears. It does. In fact it looks great, I can also capture the doneButtonAction() event which is great, but I am only capturing the UIBarButtonItem so I can't use UITextField.resignFirstResponder() method, even though I know it will work. I have to use "view"
What I want to do is call alert.endEditting(true) but I've tried cycling through the subviews, screening for any occurrences of UIAlertController but self.present doesn't work that way
Can anyone help me clear this keyboard off the alert view?
I fixed it by getting the topMost view Controller, and calling endEditing(true) I think this question should remain up it's quite a useful titbit for someone one day
#objc func doneButtonAction(_ btn: UIBarButtonItem){
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.view.endEditing(true)
}
}
My Custom Activity indicator is not overlapping Navigation controller.
Below is my code
func showActivityIndicator(uiView: UIView) {
container.frame = uiView.frame
container.center = uiView.center
container.backgroundColor = UIColorFromHex(0xffffff, alpha: 0.1)
var loadingView: UIView = UIView()
loadingView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
loadingView.center = uiView.center
loadingView.backgroundColor = UIColorFromHex(0x444444, alpha: 0.5)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
var imageData = NSData(contentsOfURL: NSBundle.mainBundle()
.URLForResource("synch-loader", withExtension: "gif")!)
let try = UIImage.animatedImageWithData(imageData!)
var imageView = UIImageView(image: try)
imageView.center = uiView.center
imageView.frame = CGRect(x: uiView.frame.width/4, y: uiView.frame.height/2, width: 500, height: 15)
loadingView.addSubview(imageView)
container.addSubview(loadingView)
uiView.addSubview(container)
actInd.startAnimating()
}
func navigationHandling()
{
if self.navigationController != nil {
self.navigationController?.navigationBar.tintColor = utility.uicolorFromHex(0x70B420)
self.navigationController?.navigationBar.barTintColor = utility.uicolorFromHex(0x70B420)
self.navigationController?.navigationBarHidden = false
self.navigationItem.hidesBackButton = true
}
sortingBtn = UIBarButtonItem(image: sortingImg, style: UIBarButtonItemStyle.Done, target: self, action: Selector("sortingPressed:"))
menuBtn = UIBarButtonItem(image: menuImg, style: UIBarButtonItemStyle.Plain, target: self, action : nil)
sortingBtn.tintColor = UIColor.whiteColor()
menuBtn.tintColor = UIColor.whiteColor()
var buttons : NSArray = [menuBtn,sortingBtn]
self.navigationItem.rightBarButtonItems = buttons as [AnyObject]
self.navigationItem.setRightBarButtonItems([menuBtn,sortingBtn], animated: true)
networkLabel.hidden = true
}
I just want to overlap the view on navigation controller so that it don't looks ugly.
I appreciate help!
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
let window = appDelegate?.window
window.addSubview("yourActivityIndicator")
Here add the activity indicator to the window, or pass the window in as the view.
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
let window = appDelegate?.window
showActivityIndicator(window)
The uiView you're passing as parameter to this method
func showActivityIndicator(uiView: UIView)
is your ViewController's view or your UINavigationController's view?
Because if it's your UIViewController's view your custom loading will take it's size and origin, that is under your NavigationBar
In iOS7 the search icon and place holder text are always appearing in middle.I tried changing the text alignment of the text field in search bar to left but it didn't work.
Is there any way to make the searchbar search icon appear left aligned in iOS7
I tried the following lines but it did not work:
UITextField *txfSearchField = [_searchBar valueForKey:#"_searchField"];
txfSearchField.textAlignment = NSTextAlignmentLeft;
[_searchArticle setImage:image forSearchBarIcon:UISearchBarIconSearch state:UIControlStateNormal];
UIimageView *im = [UIImageView alloc]init];
im.image = image;
txfSearchField.leftView =im;
There is no way to change the alignment of search bar's placeholder text, I tried so many "SO" answer but no one was working. Finally I ended with this workaround
Give some white space in right of placeholder text
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
// Load resources for iOS 6.1 or earlier
self.searchBar.placeholder = #"hello";
} else {
// Load resources for iOS 7 or later
self.searchBar.placeholder = #"hello ";
}
Enjoy !!
In Swift-3 :-
Magnify icon can be Left alignment by adding the subview over searchBar and nullify default Magnify icon.
let width = 20
let height = 20
func changeSearchBarIcon() {
let topView: UIView = searchBar.subviews[0] as UIView
var searchField:UITextField!
for subView in topView.subviews {
if subView is UITextField {
searchField = subView as! UITextField
break
}
}
if ((searchField) != nil) {
let leftview = searchField.leftView as! UIImageView
let magnifyimage = leftview.image
let imageView = UIImageView(frame: CGRect(x: searchBar.frame.origin.x , y: 10, width: width, height: height ) )
imageView.image = magnifyimage
searchField.leftView = UIView(frame: CGRect(x: 0 , y: 0, width: width, height: height) )
searchField.leftViewMode = .always
searchField.superview?.addSubview(imageView)
}
}
This is not a good solution but it works. (Test on iOS 9)
import UIKit
class MySearchBar: UISearchBar {
override func layoutSubviews() {
super.layoutSubviews()
if let l = getTextFieldLabel() {
l.addObserver(self, forKeyPath: "frame", options: .New, context: nil)
}
if let i = getIcon() {
i.addObserver(self, forKeyPath: "frame", options: .New, context: nil)
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let l = object as? UILabel {
l.removeObserver(self, forKeyPath: "frame")
l.frame = CGRect(x: 29, y: 1, width: 323, height: 25)
l.addObserver(self, forKeyPath: "frame", options: .New, context: nil)
}
if let i = object as? UIImageView {
i.removeObserver(self, forKeyPath: "frame")
i.frame = CGRect(x: 8, y: 7.5, width: 13, height: 13)
i.addObserver(self, forKeyPath: "frame", options: .New, context: nil)
}
}
func getTextFieldLabel() -> UILabel? {
for v in self.allSubViews(self) {
if NSStringFromClass(v.dynamicType) == "UISearchBarTextFieldLabel" {
return v as? UILabel
}
}
return nil
}
func getIcon() -> UIImageView? {
for v in self.allSubViews(self) {
if NSStringFromClass(v.dynamicType) == "UIImageView" {
return v as? UIImageView
}
}
return nil
}
func allSubViews( v : UIView!) -> [UIView] {
var views : [UIView] = []
views += v.subviews
for s in v.subviews {
views += allSubViews(s)
}
return views
}
}
1 subclass a uitextField
2 in initWithFrame
[self setBackgroundColor:[UIColor clearColor]];
[self setTextAlignment:NSTextAlignmentLeft];
[self setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
[self setAutocorrectionType:UITextAutocorrectionTypeNo];
[self setAutocapitalizationType:UITextAutocapitalizationTypeNone];
[self setPlaceholder:#"Search"];
[self setLeftView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"search-icon"]]];
[self setLeftViewMode:UITextFieldViewModeAlways];
3 in main class include UItextfieldDelegate and implement following delegate method
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString* searchString = [textField.text stringByReplacingCharactersInRange:range withString:string];
searchYourFriendsArray = [[NSMutableArray alloc] init];
if(searchString.length > 0 )
{
NSMutableArray *searchArray = [NSMutableArray arrayWithArray:yourFriendsArray];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"FirstName beginswith[C] %# OR LastName beginswith[C] %#",searchString,searchString];
searchYourFriendsArray = [NSMutableArray arrayWithArray:[searchArray filteredArrayUsingPredicate:predicate]];
}
return YES;
}
Workaround for iOS 9:
extension ViewController: UISearchBarDelegate {
func searchBarShouldBeginEditing(searchBar: UISearchBar) -> Bool {
if let searchTextField = searchBar.valueForKey("_searchField") as? UITextField {
if let placeholderLabel = searchTextField.valueForKey("_placeholderLabel") as? UILabel {
if searchTextField.textColor == placeholderLabel.textColor {
searchTextField.text = ""
searchTextField.textColor = UIColor.blackColor()
searchTextField.clearButtonMode = .Always
}
}
}
return true
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
if searchBar.text == "" {
if let searchTextField = searchBar.valueForKey("_searchField") as? UITextField {
if let placeholderLabel = searchTextField.valueForKey("_placeholderLabel") as? UILabel {
searchBar.text = "Placeholder"
searchTextField.textColor = placeholderLabel.textColor
searchTextField.clearButtonMode = .Never
}
}
}
searchBar.resignFirstResponder()
}
}
You can call this method, and it looks like screenshot:
+(void)customizeDisplayForSeachbar:(UISearchBar *)searchBar
{
searchBar.barTintColor = [UIColor whiteColor];
searchBar.layer.borderWidth = 0.0;
searchBar.layer.borderColor = [UIColor clearColor].CGColor;
UITextField *txfSearchField = [searchBar valueForKey:#"_searchField"];
UIImage *imageIcon=[UIImage imageNamed:#"search_icon.png"];
float iconWidth=14;
UIImageView *imgView=[[UIImageView alloc] initWithFrame:CGRectMake(15, (txfSearchField.superview.frame.size.height-iconWidth)/2-1, iconWidth, iconWidth)];
imgView.contentMode=UIViewContentModeScaleAspectFit;
imgView.image=imageIcon;
txfSearchField.leftView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 40)];
txfSearchField.leftViewMode=UITextFieldViewModeAlways;
[txfSearchField.superview addSubview:imgView];
}