Present a loading view that blocks touches - ios

I'm trying to add a subview to view that displays an activity indicator and blocks touches while I'm fetching data from the server.
Here is the code that adds the loading view to the main view controller's screen:
+ (void)showLoadingView {
ViewController *vc = [AppDelegate mainViewController];
if (!vc._activityViewContainer) {
LoadingView *loadingView = [[LoadingView alloc] initWithFrame:vc.view.bounds];
[vc.view addSubview:loadingView];
vc._activityViewContainer = loadingView;
}
}
+ (void)stopLoadingView {
ViewController *vc = [AppDelegate mainViewController];
if (vc._activityViewContainer) {
[vc._activityViewContainer removeFromSuperview];
vc._activityViewContainer = nil;
}
}
Here is my loading view:
#implementation LoadingView
- (id)initWithFrame:(CGRect)frame {
if (self == [super initWithFrame:frame]) {
self.userInteractionEnabled = YES;
self.backgroundColor = [UIColor lightGrayColor];
self.alpha = 0.6;
self.layer.zPosition = 10000;
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
NSUInteger avSize = 100;
CGRect activityViewFrame = CGRectMake((frame.size.width - avSize) / 2, (frame.size.height - avSize) / 2, avSize, avSize);
activityView.frame = activityViewFrame;
[self addSubview:activityView];
[activityView startAnimating];
}
return self;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return [self pointInside:point withEvent:event] ? self : nil; }
#end
I'm trying to make it so that the loading view absorbs/blocks all touches to other sibling views. It's working fine when I'm testing it by presenting it without doing any fetching, it enters the loading view's hitTest method and blocks the touch. However, when I'm actually doing any kind of fetch from the server, it doesn't seem to work. The touch is delayed a few seconds and does not get intercepted by the LoadingView.
I'm using GTMHTTPFetcher's
- (BOOL)beginFetchWithCompletionHandler:(void (^)(NSData *data, NSError *error))handler
I'm not that sure what's going on and I tried looking for similar examples on SO, but couldn't find what I was looking for. Any help would be great, thanks!

I was doing my fetches on the main thread which was blocking the UI; I thought GTM did everything on a different thread, but it doesn't. Thanks to those who posted things which helped me in the right direction

Related

Disabling selection in the background when activity indicator is present

I am adding activity indicator on top of the view and wish to disable the selections in the background when the activity indicator is on. Also for some reason, my activity indicator is still spins for about 30-45 seconds(depending on the network speed) after the data is displayed on the table view. I have created a category for activity indicator.
Activity Indicator category code:
- (UIView *)overlayView {
return objc_getAssociatedObject(self, OverlayViewKey);
}
- (void)setOverlayView:(UIView *)overlayView {
objc_setAssociatedObject(self, OverlayViewKey, overlayView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)showActivityIndicatorForView:(UIView *)view {
self.overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
self.center = self.overlayView.center;
[view setUserInteractionEnabled:NO];
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[self.overlayView setUserInteractionEnabled:NO];
[self startAnimating];
[self.overlayView addSubview:self];
[view addSubview:self.overlayView];
[view bringSubviewToFront:self.overlayView];
self.hidesWhenStopped = YES;
self.hidden = NO;
}
- (void)hideActivityIndicatorForView:(UIView *)view {
[self stopAnimating];
[self.overlayView setUserInteractionEnabled:YES];
[self.overlayView removeFromSuperview];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
[view setUserInteractionEnabled:YES];
}
Usages in table view controller:
#interface MyTableViewController()
#property (nonatomic, strong) UIActivityIndicatorView *activityIndicator;
#end
#implementation MyTableViewController
- (id) initWithSomething:(NSString *)something {
self = [super init];
if (self) {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.activityIndicator.overlayView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self getDataServiceRequest];
[self.activityIndicator showActivityIndicatorForView:self.navigationController.view];
}
- (void)requestCompletionCallBack sender:(ServiceAPI *)sender {
// Do something here with the data
[self.activityIndicator hideActivityIndicatorForView:self.navigationController.view];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
#end
What am I doing wrong here? Why am I still able to select the data in the background when the activity indicator is on and even after disabling the user interaction.
Move your call to hideActivityIndicatorForView to inside the call to dispatch_async(dispatch_get_main_queue(). It's a UI call, and needs to be done on the main thread.
As for how to disable other actions on your view controller, you have a few options. One simple thing I've done is the put the activity indicator inside a view that's pinned to the whole screen, set to opaque=false, and with a color that's black with an alpha setting of 0.5. That way the content underneath is visible but the user can't click on it. You need to add an outlet to your "coveringView" and show-hide it instead of showing/hiding the activity indicator view.
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
fix it
[self performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];

how to hide systems status bar and show alert in status bar for few sec by animation in iOS 9

I want to show alert in status bar for small duration with animation and hide systems status bar for that duration
I have referred this but enable to hide system's status bar for that particular time,failed to add animation
Here is my code
NSString *status=#"welcome..";
UIView *notificationView= [JDStatusBarNotification showWithStatus:(NSString *)status styleName:JDStatusBarStyleDark];
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
notificationView.frame=statusBarFrame;
[self.view addSubview:notificationView];
UIView *dismissNotificationView=[JDStatusBarNotification showWithStatus:(NSString *)status
dismissAfter:(NSTimeInterval)5.0f styleName:JDStatusBarStyleDark];
[self.view addSubview:dismissNotificationView]; `
also tried this but it moves another window then shows and turns back
here is the code used
MTStatusBarOverlay *overlay = [MTStatusBarOverlay sharedInstance];
overlay.animation = MTStatusBarOverlayAnimationFallDown; // MTStatusBarOverlayAnimationShrink
overlay.detailViewMode = MTDetailViewModeHistory; // enable automatic history-tracking and show in detail-view
overlay.delegate = self;
overlay.progress = 0.0;
[overlay postImmediateFinishMessage:#"welcome" duration:2.0 animated:YES];
overlay.progress = 1.0;
please help..thanks in advance
I'm not sure when you want the alert to appear, so I will assume just after loading the view. I didn't use the two frameworks, but tested it with a generic red UIView and it worked with the code below:
#import "ViewController.h"
#interface ViewController () {
BOOL animating;
CGRect aimFrame;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
animating = YES; //To set if we are showing the alert or not
aimFrame = [[UIApplication sharedApplication] statusBarFrame];
[self setNeedsStatusBarAppearanceUpdate];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIView *notificationView = [[UIView alloc] init];
[notificationView setFrame:aimFrame];
[notificationView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:notificationView];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[notificationView removeFromSuperview];
animating = NO;
[self setNeedsStatusBarAppearanceUpdate];
});
}
- (BOOL)prefersStatusBarHidden {
// Toggle based upon if we are showing the alert
if (animating) {
return YES;
} else {
return NO;
}
}
#end
I hope this helps, let me know if it still doesn't work or if I miss understood something :)
Try my code to complete hide the status bar:
-(BOOL)prefersStatusBarHidden{
return YES;
}

Custom UIView getting display late

I'm facing an weird issue, I'm developing a iOS static library, this static library have some custom UIViews. These custom views contains UIButtons and IBOutlets for those buttons, Custom UIView has been create in Xib. Now this static library I'm including inside another static library and I'm attaching custom UIView
of library one in to UIViewController's UIView of static library two with -
[self.view addSubview:aCustomViewFromLibrary1];
So when I'm using Library 2 in to some product everything goes great but when code of adding custom UIView of static library one to static library two runs everything goes right but custom UIView appears late, about half a minutes late.
here is the code for one of the custom UIView in static library one...
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(!self){
return nil;
}
_otpTextField.delegate = self;
loadView()
_approveOtpBtn.layer.cornerRadius = 5;
NSLog(#"%#",[NSString stringWithFormat:#"%s", __PRETTY_FUNCTION__]);
viewFrame = frame;
CALayer *layer = self.layer;
layer.shadowOffset = CGSizeMake(1, 1);
layer.shadowColor = [[UIColor blackColor] CGColor];
layer.shadowRadius = 4.0f;
layer.shadowOpacity = 0.80f;
layer.shadowPath = [[UIBezierPath bezierPathWithRect:layer.bounds] CGPath];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(!self){
return nil;
}
loadView()
_approveOtpBtn.layer.cornerRadius = 5;
NSLog(#"%#",[NSString stringWithFormat:#"%s", __PRETTY_FUNCTION__]);
return self;
}
-(void) awakeFromNib{
NSLog(#"%#",[NSString stringWithFormat:#"%s", __PRETTY_FUNCTION__]);
_approveOtpBtn.layer.cornerRadius = 5;
}
// Approve OTP
-(IBAction)approveOtp:(UIButton *) aButton{
NSLog(#"%#",[NSString stringWithFormat:#"%s", __PRETTY_FUNCTION__]);
}
// minimize custome browser
-(IBAction)minimizeCB:(UIButton *) aButton{
NSLog(#"%#",[NSString stringWithFormat:#"%s", __PRETTY_FUNCTION__]);
}
- (void) RegenerateOTP:(UIButton *) aButton{
NSLog(#"%#",[NSString stringWithFormat:#"%s", __PRETTY_FUNCTION__]);
}
------UPDATE------
This is how I'm adding above custom View in the UIViewController's UIView.
- (void) updateView{
if([paymentOption isEqualToString:CHOOSE]){
if(_choose){
[_choose removeFromSuperview];
_choose = nil;
}
if(_approveOTP){
[_approveOTP removeFromSuperview];
_approveOTP = nil;
}
if(_regenOTPView){
[_regenOTPView removeFromSuperview];
_regenOTPView = nil;
}
_choose = [[CBAllPaymentOption alloc] initWithFrame:CGRectMake(0,self.resultView.frame.size.height - 227,self.resultView.frame.size.width,227)];
_choose.bankJS = _bankSpecificJavaScriptDict;
_choose.handler = self;
NSLog(#"loadJavascript AllOptionView view = %# ResultView = %#",_choose,_resultView);
//[_resultView addSubview:_choose];
if(_connectionHandlerDelegate && [_connectionHandlerDelegate respondsToSelector:#selector(addViewInResultView:)]){
[_connectionHandlerDelegate addViewInResultView:_choose];
}
[_resultView bringSubviewToFront:_choose];
// view getting display late so call setNeedDisplay.
[_choose setNeedsDisplay];
_choose.isViewOnScreen = YES;
}
}
Any solution and suggestion are welcome
Any changes you make to the UI must be done on the main thread.
You can try something like this.
dispatch_async(dispatch_get_main_queue(), ^{
// perform updates here
});

Using UIKit's UIScrollView with SpriteKit

I read two questions here about that, both with the same answer. The suggested solution was to create the UIScrollView object, and then add it to the parent view of the SKScene in the target scene's didMoveToView:, removing it in willMoveFromView:.
And that is the target scene code I implemented:
#interface MQSSMakerScene ()
#property BOOL contentCreated;
#property MQSSMakerWorkspaceLayer *workspaceLayer;
#property MQSSMakerDockLayer *dockLayer;
#property UIScrollView *scrollView;
#end
#implementation MQSSMakerScene
- (id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
CGSize layerSize = CGSizeMake(944, 140);
CGPoint layerPosition = CGPointMake(40, 768-(140+30));
CGRect viewFrame = CGRectMake(layerPosition.x, layerPosition.y, layerSize.width, layerSize.height);
_scrollView = [[UIScrollView alloc] initWithFrame:viewFrame];
_scrollView.contentSize = CGSizeMake(2000, 120);
_scrollView.scrollEnabled = YES;
_scrollView.showsHorizontalScrollIndicator = YES;
_scrollView.backgroundColor = [UIColor brownColor];
}
return self;
}
- (void)didMoveToView:(SKView *)view
{
[super didMoveToView:view];
if(!self.contentCreated) {
[self createSceneContents];
self.contentCreated = YES;
}
[self.view addSubview:_scrollView]; // --> WHERE IT'S BEING ADDED
}
- (void)willMoveFromView:(SKView *)view
{
[super willMoveFromView:view];
[_scrollView removeFromSuperview]; // --> WHERE IT'S BEING REMOVED
}
- (void)createSceneContents
{
self.backgroundColor = [UIColor colorWithHue:.237 saturation:.56 brightness:.46 alpha:1];
self.scaleMode = SKSceneScaleModeAspectFill;
[self addChild:[self newMakerLayout]];
}
- (SKNode *)newMakerLayout
{
SKNode *mainLayout = [[SKNode alloc] init];
self.workspaceLayer = [[MQSSMakerWorkspaceLayer alloc] init];
self.dockLayer = [[MQSSMakerDockLayer alloc] init];
[mainLayout addChild:self.workspaceLayer];
[mainLayout addChild:self.dockLayer];
MQSSStoryObject *ob1 = [[MQSSStoryObject alloc] initWithImageNamed:#"maker-1.png"];
[self.workspaceLayer addChild:ob1];
return mainLayout;
}
#end
Please note that the user should go to this scene from the home scene when clicking on a button. The problem now is that once I add the line:
[self.view addSubview:_scrollView];
to add the UIScrollView object to this scene, the application no more goes to this scene and instead add the UIScrollView to the home scene I am transitioning from. It also doesn't remove it when transitioning to another scene. Once I comment out this line, everything works just as expected, clearly without presenting the UIScrollView.
I am stuck. Any help or advice is highly appreciated. Thanks!
I finally figured out what the problem was for me. I was using the -(void)willLayoutSubviews method of the viewController to ensure I had the correct bounds, but I had forgotten to see if my SKScene was already presented. That resulted in a new welcomeScene being presented each time the view was laid out on top of the old one.
-(void)willLayoutSubviews
{
if(!self.didPresentScene)
{
// Present scene here
....
self.didPresentScene = YES;
}
}

UIScrollView costum pagination size

first take a look on this picture from localScope app :
i have 2 (simple?) questions :
how can i paginate my icons like this?
how can i detect witch icon is " selected "
thank you.
Answer to the first question: You have to make your scroll view as big as the page size, enable its pagingEnabled property, then somehow make it to display elements and respond to touches outside of its bounds. See this code and these links:
#interface SmallPagedScrollView: UIScrollView <UIScrollViewDelegate> {
UIEdgeInsets responseInsets;
NSMutableArray *items;
}
#implementation SmallPagedScrollView
#synthesize responseInsets;
- (id)init
{
if ((self = [super initWithFrame:CGRectMake(x, y, w, h)]))
{
self.backgroundColor = [UIColor clearColor];
self.pagingEnabled = YES;
self.showsHorizontalScrollIndicator = NO;
self.showsVerticalScrollIndicator = NO;
self.clipsToBounds = NO;
CGFloat hInset = 3 * self.width / 2;
self.responseInsets = UIEdgeInsetsMake(0.0f, hInset, 0.0f, hInset);
self.delegate = self;
items = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc
{
[items release];
[super dealloc];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint parentLocation = [self convertPoint:point toView:self.superview];
CGRect responseRect = self.frame;
responseRect.origin.x -= self.responseInsets.left;
responseRect.origin.y -= self.responseInsets.top;
responseRect.size.width += self.responseInsets.left + self.responseInsets.right;
responseRect.size.height += self.responseInsets.top + self.responseInsets.bottom;
return CGRectContainsPoint(responseRect, parentLocation);
}
See also Paging UIScrollView in increments smaller than frame size (Split's answer)
Answer to the second question: you can calculate the selected page using this formula:
int selectedIndex = (scrollView.contentOffset + scrollView.size.width / 2) / scrollView.size.width;
Well one clean & memory efficient approach is to have a UINavigationController & UIToolBar like so -
When the user taps on any button in the UIToolBar invoke that particular viewController by popping and pushing them.
I hope its clear that the look and feel can be achieved close to what you are showing in the image, I am talking about the functionality.

Resources