Replace UIBarButtonItem with UIActivityIndicatorView - objective-c

I want to replace my UIBarButtonItem (used for refresh) with a UIActivityIndicatorView and, when the refresh is finished, I want to turn back to the refresh button and remove the UIActivityIndicatorView.

Just create two different UIBarButtonItems
One for the activity indicator and another for a normal UIBarButtonItem.
UIActivityIndicatorView * activityView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 25, 25)];
[activityView sizeToFit];
[activityView setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin)];
UIBarButtonItem *loadingView = [[UIBarButtonItem alloc] initWithCustomView:activityView];
[self.navigationItem setRightBarButtonItem:loadingView];
[loadingView release];
[activityView release];
UIBarButtonItem * normalButton = [[UIBarButtonItem alloc] initWithTitle...];
[self.navigationItem setRightBarButtonItem:normalButton];
[normalButton release];
When you want to switch them, just reassign the rightBarButtonItem to whichever.

Updated for Swift 5.2, Xcode 11.4
class ViewController: UIViewController {
var activityIndicator = UIActivityIndicatorView()
var refreshBarButton = UIBarButtonItem()
var activityBarButton = UIBarButtonItem()
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.sizeToFit()
activityIndicator.color = self.view.tintColor
activityBarButton = UIBarButtonItem(customView: activityIndicator)
refreshBarButton = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refreshBarButtonPressed))
showRefreshButton()
}
func performNetworkOperation(completion: #escaping()->()) {
//simulate network operation
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
completion()
})
}
#objc func refreshBarButtonPressed() {
showActivityIndicator()
activityIndicator.startAnimating()
performNetworkOperation {
self.activityIndicator.stopAnimating()
self.showRefreshButton()
}
}
func showRefreshButton() {
self.navigationItem.setRightBarButton(refreshBarButton, animated: true)
}
func showActivityIndicator() {
self.navigationItem.setRightBarButton(activityBarButton, animated: true)
}
}

Here what works for me :
- (void) rightItemButtonWithActivityIndicator
{
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
[activityIndicator startAnimating];
UIBarButtonItem *activityItem = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
[activityIndicator release];
self.navigationItem.rightBarButtonItem = activityItem;
[activityItem release];
}

I was trying to do the same thing, and I thought setting self.navigationItem.rightBarButtonItem didn't work because the activity indicator wouldn't display. Turns out it was working fine, I just couldn't see it because I have a white navigation bar and the default UIActivityIndicatorView style is also white. So it was there but invisible. With the gray style I can now see it.
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
(Duh.)

I used a similar technique to update a button in a UIToolbar when a UIWebView is reloading (as it doesn't appear to be possible to show/hide individual bar button items). In this case, you need to swap out all of the items in the UIToolbar.
#property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshBarButton;
#property (nonatomic, strong) UIActivityIndicatorView *activityView;
#property (nonatomic, strong) UIBarButtonItem *activityBarButton;
#property (strong, nonatomic) IBOutlet UIToolbar *toolbar;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *backBarButton;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *refreshBarButton;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *forwardBarButton;
#pragma mark - UIWebViewDelegate
-(void)webViewDidFinishLoad:(UIWebView *)webView{
[self updateButtons];
}
-(void)webViewDidStartLoad:(UIWebView *)webView{
[self updateButtons];
}
-(void)updateButtons{
/*
It's not possible to show/hide bar button items so we need to do swap out the toolbar items in order to show the progress view
*/
//Initialise the activity view
if (self.activityBarButton == nil){
self.activityView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
self.activityBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.activityView];
self.activityBarButton.enabled = NO;
}
NSMutableArray *toolbarItems = [[NSMutableArray alloc] initWithArray:self.toolbar.items];
if ([self.webview isLoading]){
//Replace refresh button with loading spinner
[toolbarItems replaceObjectAtIndex:[toolbarItems indexOfObject:self.refreshBarButton]
withObject:self.activityBarButton];
//Animate the loading spinner
[self.activityView startAnimating];
}
else{
//Replace loading spinner with refresh button
[toolbarItems replaceObjectAtIndex:[toolbarItems indexOfObject:self.activityBarButton]
withObject:self.refreshBarButton];
[self.activityView stopAnimating];
}
//Set the toolbar items
[self.toolbar setItems:toolbarItems];
//Update other buttons
self.backBarButton.enabled = [self.webview canGoBack];
self.forwardBarButton.enabled = [self.webview canGoForward];
}

Related

-[UIPopoverController dealloc] reached while popover is still visible

I have a class (ViewOpenAppointments) where I create and display a UIPopover. This is the code to define the popover in my .h file:
#interface ViewOpenAppointments : UIView {
}
#property (nonatomic, retain) UIPopoverController *popoverController;
-(void)createOpenAppointmentsPopover: (UIButton *) obViewOpenAppts;
#end
I have a check in the code that if the popover is visible, dismiss it. This is the code:
// create popover
UIViewController* popoverContent = [[UIViewController alloc] init];
// UIView *popoverView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 650, 416)];
ViewOpenAppointments *popoverView = [[ViewOpenAppointments alloc] initWithFrame:CGRectMake(0, 0, 650, 416)];
popoverView.backgroundColor = [UIColor whiteColor];
popoverContent.preferredContentSize = CGSizeMake(650.0, 416.0);
// create the popover controller
popoverController = [[UIPopoverController alloc] initWithContentViewController:popoverContent];
popoverController.delegate = (id)self;
[popoverController setPopoverContentSize:CGSizeMake(650, 416) animated:NO];
if ([popoverController isPopoverVisible]) {
[popoverController dismissPopoverAnimated:YES];
}
[popoverController presentPopoverFromRect:CGRectMake(650, 416, 10, 50) inView: obViewOpenAppts
permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
The problem is that the code to dismiss the popover is never hit, which means it's not visible. But I still get the error message (described in the question title).
What am I doing wrong?
Here is a complete popover management example:
#interface ViewController () <UIPopoverControllerDelegate>
#property (nonatomic, strong) UIPopoverController* currentPop;
#end
#implementation ViewController
-(IBAction)doPopover1:(id)sender {
Popover1View1* vc = [[Popover1View1 alloc] initWithNibName:#"Popover1View1" bundle:nil];
UIPopoverController* pop = [[UIPopoverController alloc] initWithContentViewController:vc];
self.currentPop = pop;
[pop presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
pop.passthroughViews = nil;
// make ourselves delegate so we learn when popover is dismissed
pop.delegate = self;
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)pc {
self.currentPop = nil;
}
By checking self.currentPop you can make sure you don't present two popovers at once (illegal anyway).

Why UIBarButtonItem not getting clicks in UIToolbar set as inputAccessoryView of UITextField?

UIBarItem does not respond to clicks inside UIToolbar which is setup as inputAccessoryView on a UITextField.
The button does not show click animation when I try to click it, callback does not get called.
My setup looks like:
#interface MyViewController()
#property (weak, nonatomic) IBOutlet UITextField *closeDateTextField;
#property (strong, nonatomic) UIToolbar * datePickerToolbar;
#end
I setup toolbar with button:
- (void)viewDidLoad {
self.datePickerToolbar = [[UIToolbar alloc] init];
UIBarButtonItem * doneBtn =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector(hidePicker:)];
add button to toolbar and set toolbar as inputAccessoryView of UITextField:
[self.datePickerToolbar setItems:#[doneBtn] animated:NO];
self.closeDateTextField.inputAccessoryView = self.datePickerToolbar;
}
When I click on closeDateTextField the keyboard appears with a Done button in toolbar but the button does not respond to click, the hidePicker: does not get called.
- (void)hidePicker:(id)sender {
[self.closeDateTextField resignFirstResponder];
}
Any idea what I'm doing wrong?
I just tried the following code and it worked just fine. Tested on both iOS 6 & 7 simulator.
#interface HSViewController
#property (weak, nonatomic) IBOutlet UITextField *closeDateTextField;
#property (strong, nonatomic) UIToolbar * datePickerToolbar;
#end
#implementation HSViewController
- (void)viewDidLoad
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[super viewDidLoad];
self.datePickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
UIBarButtonItem * doneBtn =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector(hidePicker:)];
[self.datePickerToolbar setItems:#[doneBtn] animated:NO];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 120)];
textField.backgroundColor = [UIColor greenColor];
[self.view addSubview:textField];
self.closeDateTextField = textField;
self.closeDateTextField.inputAccessoryView = self.datePickerToolbar;
}
- (void)hidePicker:(id)sender {
NSLog(#"%s", __PRETTY_FUNCTION__);
[self.closeDateTextField resignFirstResponder];
}
#end
self.datePickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 43)];
It worked with me when I did the toolbar height less than 44. 43 for an example
when it was 44 it crashes
Instead of setting the UIToolbar frame you should only set its autoresizingMask:
self.datePickerToolbar.autoresizingMask =
UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
This way you'll have the expected behavior while being device/orientation independent.

Mirror text from UITextField on inputAccessoryView - UIToolBar to text on UITextField on navigationController.toolbar

In my app I have a UITextField on the navigationController toolbar.
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic,strong) NSArray *toolBarButtonItems;
#property (nonatomic,strong) UITextField *textField;
#property (nonatomic,strong) UITextField *textField2;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.textField = [[UITextField alloc]initWithFrame:CGRectMake(0, 0, 60, 40)];
self.textField.delegate = self;
self.textField.borderStyle = UITextBorderStyleRoundedRect;
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]initWithCustomView:self.textField];
self.toolBarButtonItems = #[flexibleSpace,barButtonItem,flexibleSpace];
self.toolbarItems = self.toolBarButtonItems;
self.navigationController.toolbar.barTintColor = [UIColor blueColor];
[self.navigationController setToolbarHidden:NO animated:NO];
}
When the textField is clicked the keyboard opens up and I create a new inputAccessoryView toolbar with another textField.
-(UIToolbar *)addToolBar{
UIToolbar *toolbar = [[UIToolbar alloc]initWithFrame:self.navigationController.toolbar.frame];
toolbar.barTintColor = [UIColor darkGrayColor];
self.textField2 = [[UITextField alloc]initWithFrame:CGRectMake(0, 0, 60, 40)];
self.textField2.delegate = self;
self.textField2.borderStyle = UITextBorderStyleRoundedRect;
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]initWithCustomView:self.textField2];
[toolbar setItems:#[flexibleSpace,barButtonItem,flexibleSpace]];
return toolbar;
}
The idea is to change the firstResponder to the textField on the inputAccessoryView so this way I can see what I'm editing. The reason I am doing this is cause I can't scroll the Navigation toolbar up past the keyboard and I want to see the text that I am editing.
-(void)textFieldDidBeginEditing:(UITextField *)textField{
textField.inputAccessoryView = [self addToolBar];
if(self.textField2.isFirstResponder != NO){
[self.textField2 becomeFirstResponder];
}
}
It doesn't seem to be working when I click on the textField in the navigationController toolbar. The new inputAccessoryView toolbar shows up over the keyboard but I can't edit the field because the responder doesn't seem to be changing. The return key doesn't work either. I have to hit it twice in order to close the keyboard and when I do the text doesn't match up between the two text fields.
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
self.textField.text = self.textField2.text;
return YES;
}
I got it to work like this:
#import "KJMViewController.h"
#interface KJMViewController ()
#property (strong, nonatomic) UITextField *textField1;
#property (strong, nonatomic) UITextField *textField2;
#end
#implementation KJMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.textField1 = [[UITextField alloc]initWithFrame:CGRectMake(30, 7, 260, 30)];
self.textField1.borderStyle = UITextBorderStyleRoundedRect;
self.textField1.delegate = self;
UIToolbar *navToolbar = self.navigationController.toolbar;
[navToolbar addSubview:self.textField1];
UIToolbar *toolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
self.textField2 = [[UITextField alloc]initWithFrame:CGRectMake(30, 7, 260, 30)];
self.textField2.borderStyle = UITextBorderStyleRoundedRect;
self.textField2.delegate = self;
[toolbar addSubview:self.textField2];
self.textField1.inputAccessoryView = toolbar;
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(firstRes:) name:UIKeyboardDidShowNotification object:nil];
}
- (void)firstRes:(id)sender
{
[self.textField2 becomeFirstResponder];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.textField2) {
self.textField1.text = self.textField2.text;
}
[textField resignFirstResponder];
[self.textField1 resignFirstResponder];
return YES;
}
- (void)viewDidDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter]removeObserver:self forKeyPath:UIKeyboardDidShowNotification];
[super viewDidDisappear:animated];
}
#end
Here's what's happening in viewDidLoad:
Initialise toolbar and textField2
Set the inputAccessory for textField1 (the one hidden by the keyboard) here so it's ready to become firstResponder
Then in the viewDidAppear method:
Sign up for a notification that's sent when the keyboard is shown. You'll then write some code in the "firstRes" method to make textField2 the firstResponder. You need to make it the firstResponder using this notification because you know that it's in the view hierarchy by this time, which means it's able to become firstResponder. Calling it in the -(void)textFieldDidBeginEditing:(UITextField *)textField seems to fire it before textField2 comes on screen, meaning that it can't become firstResponder. We sign up for it in the viewDidAppear method because we only want to get the notification if we're on screen.
After textField2 resignsFirstResponder, textField1 becomes first responder again, so you have to call resignFirstResponder twice in the textFieldShouldReturn method.
Also, if we leave the screen, we need to remove ourself as an observer of the keyboard notification in the viewDidDisappear method.
Here's a link to the project I made in Xcode so you can see how it works:
https://github.com/kylejm/UIToolBar-UITextView

Trying to fix subview flipping and now subview buttons no longer work?

I have a view which includes two subviews. I had it working so that only one subview was shown at a time and each subview had a button and when the button was clicked the subview would flip over and the next subview would appear. The problem was that it appeared as though the entire view was flipping. After reading on this site about how to solve the problem I attempted to add the subviews to a container and flip that instead. However now, although my first subview is showing up when I press the button it no longer flip. It doesn't do anything. I put a log statement in the method which flips the subviews, as well as a breakpoint and as far as I can tell it no longer gets called. I'm very new to xcode and objective c and delegates and I have no idea how to proceed. Any help would be appreciated. Thanks.
I have included the relevant code here:
The header for the ViewController
#interface ExerciseViewController : UIViewController<ExerciseSubViewDelegate>
//stuff for subviews
#property (nonatomic, strong) ExerciseSubViewImage *subViewImage;
#property (nonatomic, strong) ExerciseSubViewText *subViewText;
#property UIView *panel;
#end
This is the code for the ViewController:
#interface ExerciseViewController ()
#end
#implementation ExerciseViewController
#synthesize subViewImage, subViewText;
- (void)viewDidLoad
{
self.subViewImage.delegate = self;
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
}
-(ExerciseSubViewImage *)subViewImage
{
if (!subViewImage)
{
CGRect subViewImageFrame = CGRectMake(0,0, _panel.bounds.size.width, _panel.bounds.size.height);
self.subViewImage = [[ExerciseSubViewImage alloc] initWithFrame:subViewImageFrame];
[_panel addSubview:subViewImage];
}
return subViewImage;
}
-(ExerciseSubViewText *)subViewText
{
if (!subViewText)
{
CGRect subViewTextFrame = CGRectMake(0,0, _panel.bounds.size.width, _panel.bounds.size.height);
self.subViewText = [[ExerciseSubViewText alloc] initWithFrame:subViewTextFrame];
self.subViewText.backgroundColor = [UIColor blueColor];
[_panel addSubview:subViewText];
}
return subViewText;
}
-(void)exerciseSubViewImagePressed
{
[UIView transitionWithView:_panel
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[subViewImage removeFromSuperview];
[_panel addSubview:subViewText];
}
completion: nil];
//This is how I did it before I added the container
/*[UIView transitionFromView:subViewImage
toView:subViewText
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
self.subViewText.delegate = self;*/
NSLog(#"Ipushedtheimage");
}
-(void)exerciseSubViewTextPressed
{//I haven't updated this yet
[UIView transitionFromView:subViewText
toView:subViewImage
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
self.subViewImage.delegate = self;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
subViewImage = nil;
subViewText = nil;
}
#end
This is the code for the delegate
#import
#protocol ExerciseSubViewDelegate <NSObject>
-(void) exerciseSubViewImagePressed;
-(void) exerciseSubViewTextPressed;
#end
I am also added the code for the first subview:
#import
#import "ExerciseSubViewDelegate.h"
#interface ExerciseSubViewImage : UIView
#property (nonatomic, strong) UIButton *button;
#property (nonatomic, assign) id<ExerciseSubViewDelegate>delegate;
#property (strong, nonatomic) UIImageView *exerciseImageView;
#end
#import "ExerciseSubViewImage.h"
#import "UIImage+animatedGIF.h"
#implementation ExerciseSubViewImage
#synthesize button;
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Initialization code
self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
CGRect buttonFrame = CGRectMake(50,200,100,35);
self.button.frame = buttonFrame;
[self.button setTitle:#"Image"forState:UIControlStateNormal];
[self.button addTarget:self
action:#selector(buttonTouched)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.button];
_exerciseImageView = [[UIImageView alloc] initWithFrame:CGRectMake(50,20,160,158)];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"AppleLogo" withExtension:#"gif"];
_exerciseImageView.image = [UIImage animatedImageWithAnimatedGIFURL:url];
[self addSubview:self.exerciseImageView];
}
return self;
}
-(void)buttonTouched
{
NSLog(#"imagebuttonpressed");
[self.delegate exerciseSubViewImagePressed];
}
Again, any help would be appreciate. I know I'm probably just not understanding something simple.
Ok. This took me all weekend but I finally figured it out on my own. I thought I would shere the answer here in case anyone else ever has a similar problem. After trying several other approaches I finally went back to the approach I used here and started inserting a whole bunch of NSLogs to determine the order that every thing was executing in. What I finally ended up doing was changing this: (all in the top ViewController)
self.subViewImage.delegate = self;
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
to this:
//create panel
_panel = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.view.bounds.size.width, s self.view.bounds.size.height/2)];
_panel.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_panel];
[_panel addSubview:subViewImage];
//Set the subview delegates
self.subViewImage.delegate = self;
self.subViewText.delegate = self;

Add Background Image behind TextView

Hey guys,
Does anyone know how to make the Text View transparent and add a Background Image behind the Text View? Thanks
In viewDidLoad
textView.backgroundColor = [UIColor clearColor];
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:textView.frame] autorelease];
imageView.image = [UIImage imageNamed:#"myBackgroundImage.png"];
[self.view addSubview:imageView];
[self.view bringSubviewToFront:textView];
Try to make a new class. (Did not test the code, just typed it on the forum, try to use the code otherwise: use the concept)
#interface TextViewWithImage : UIView {
UITextView *textView;
UIImageView *imageView;
}
#property (retain, readonly) UITextView *textView;
#property (retain, readonly) UIImageView *imageView;
#implementation TextViewWithImage
- (id) init {
if (self = [super init]) {
[self setupContentViews];
}
return self;
}
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupContentViews];
}
return self;
}
- (void) setupContentViews {
textView = [[UITextView alloc] init];
imageView = [[UIImageView alloc] init];
textView.frame = self.frame;
imageView.frame = self.frame;
textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void) dealloc {
[textView release];
[imageView release];
[super dealloc];
}
#end
UIViews (of which UITextView is a subclass) have a property called backgroundColor.
#property(nonatomic, copy) UIColor *backgroundColor
Try calling [textView setBackgroundColor:[UIColor clearColor]] on the TextView and putting an image view behind it.