-------->
I am attempting to create a custom UIActivityIndicatorView. The custom view should behave the exactly the same as the standard view, except the image that it spins looks different. I noticed that my custom view would not deallocate when it is removed from its superview in the following test code:
ActivityIndicatorCustomView* v = [[ActivityIndicatorCustomView alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 100.0f, 100.0f)];
[[UIApplication sharedApplication].keyWindow addSubview:v];
[v removeFromSuperview];
The culprit is the animation block, because when it is commented out, dealloc will be called. I believe it is a retain cycle, but I don't see how to solve the issue.
ActivityIndicatorCustomView.h
#import <UIKit/UIKit.h>
#interface ActivityIndicatorCustomView : UIView
#property(nonatomic, assign, readonly) BOOL isAnimating;
- (void)startAnimating;
- (void)stopAnimating;
#end
ActivityIndicatorCustomView.m
static const NSTimeInterval ANIMATION_PERIOD_HALF_LIFE = 1.0f;
#import "ActivityIndicatorCustomView.h"
#interface ActivityIndicatorCustomView ()
#property(nonatomic, strong) UIImageView* imageView;
#property(nonatomic, assign, readwrite) BOOL isAnimating;
- (void)animateWithTransform:(CGAffineTransform)transform;
#end
#implementation ActivityIndicatorCustomView
#pragma mark NSObject
- (void)dealloc
{
NSLog(#"dealloc");
}
#pragma mark UIView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"activityIndicatorCustom"]];
self.imageView.frame = self.bounds;
[self addSubview:self.imageView];
}
return self;
}
- (void)didMoveToSuperview
{
if (!self.hidden && self.superview != nil) {
[self startAnimating];
}
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
if (newSuperview == nil) {
[self stopAnimating];
}
}
- (void)setHidden:(BOOL)hidden
{
if (hidden) {
[self stopAnimating];
} else if (self.superview != nil) {
[self startAnimating];
}
[super setHidden:hidden];
}
#pragma mark ActivityIndicatorCustomView
- (void)startAnimating
{
if (self.isAnimating) {
return;
}
self.isAnimating = YES;
[self animateWithTransform:CGAffineTransformMakeRotation((CGFloat)M_PI)];
}
- (void)stopAnimating
{
[self.imageView.layer removeAllAnimations];
self.isAnimating = NO;
}
#pragma mark ()
- (void)animateWithTransform:(CGAffineTransform)transform
{
// Must split the animation into two semi-circles. If
// you attempt to rotate a full circle, nothing will
// happen.
__block ActivityIndicatorCustomView* weakSelf = self;
[UIView
animateWithDuration:ANIMATION_PERIOD_HALF_LIFE
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
weakSelf.imageView.transform = transform;
} completion:^(BOOL finished) {
[weakSelf animateWithTransform:CGAffineTransformIsIdentity(transform)
? CGAffineTransformMakeRotation((CGFloat)M_PI)
: CGAffineTransformIdentity
];
}
];
}
#end
I was following a bad tutorial on retain cycles in blocks. It told me to do
__block MyViewController *weakSelf = self;
This is wrong. To create a weak reference, I should do this instead:
__weak ActivityIndicatorCustomView* weakSelf = self;
[UIView
animateWithDuration:ANIMATION_PERIOD_HALF_LIFE
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
weakSelf.imageView.transform = transform;
} completion:^(BOOL finished) {
[weakSelf animateWithTransform:CGAffineTransformIsIdentity(transform)
? CGAffineTransformMakeRotation((CGFloat)M_PI)
: CGAffineTransformIdentity
];
}
];
I think this is because you continue animating (and retain self in next [self animateWithTransform:] call) in the completion block. Try, for example, checking for the superview to decide whether to continue animating:
completion:^(BOOL finished) {
if (self.superview) {
[self animateWithTransform:CGAffineTransformIsIdentity(transform)
? CGAffineTransformMakeRotation((CGFloat)M_PI)
: CGAffineTransformIdentity
];
}
}
Related
I cant seem to present my custom ShareViewController. I inherited UIViewController, not SLComposeViewController, added it to the Storyboard, but when i select an image to share, ShareViewController is not presented. Here is my entire code:
Header file:
#import <UIKit/UIKit.h>
#import <Social/Social.h>
#interface ShareViewController : UIViewController
#end
Implementation file:
#implementation ShareViewController
- (void)viewDidLoad{
[super viewDidLoad];
self.view.alpha = 0;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.view.transform = CGAffineTransformMakeTranslation(0, self.view.frame.size.height);
[UIView animateWithDuration:0.25 animations:^
{
self.view.transform = CGAffineTransformIdentity;
}];
}
- (void)dismiss
{
[UIView animateWithDuration:0.20 animations:^
{
self.view.transform = CGAffineTransformMakeTranslation(0, self.view.frame.size.height);
}
completion:^(BOOL finished)
{
[self.extensionContext completeRequestReturningItems:nil completionHandler:nil];
}];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:YES];
for (NSItemProvider* itemProvider in ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments )
{
if([itemProvider hasItemConformingToTypeIdentifier:#"public.image"])
{
[itemProvider loadItemForTypeIdentifier:#"public.image" options:nil completionHandler:
^(id<NSSecureCoding> item, NSError *error)
{
UIImage *sharedImage = nil;
if([(NSObject*)item isKindOfClass:[NSURL class]])
{
sharedImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:(NSURL*)item]];
}
if([(NSObject*)item isKindOfClass:[UIImage class]])
{
sharedImage = (UIImage*)item;
}
}];
}
}
}
#end
Breakpoint indicates that runtime does execute the code, even grabs image, but the custom view controller is not presented for some reason. Any help?
It was so stupid and it took me a while to figure it out, but for some reason, by default, custom ShareViewController view's background color is set to [UIColor clearColor]. Very stupid of Apple :), but i solved this case
I have one viewcontroler with one button.When i press that one pop up viewcontroller will show up.And when i touch anywhere outside it dismiss the viewcontroller .And return to my main viewcontroller.
Note
I used my popup through one viewcontroller and i use identifier name .Then i show pop up using that storyboard identifier and declared in my viewcontroller.m in btnSelectDatePressed
Needed
in that pop up view controller i have one button when user press also it should dismiss the viewcontroller and should return to my main viewcontroller.My example image is like this sample image
This is my viewcontroller.m file
#import "ViewController.h"
#import "UIViewController+ENPopUp.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)btnSelectDatePressed:(id)sender
{
UIViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"PopUp"];
vc.view.frame = CGRectMake(0, 0, 309.0f, 531.0f);
[self presentPopUpViewController:vc];
}
This is my popup method declared in #import "UIViewController+ENPopUp.h"
#import <UIKit/UIKit.h>
#interface UIViewController (ENPopUp)
#property (nonatomic, retain) UIViewController *en_popupViewController;
- (void)presentPopUpViewController:(UIViewController *)popupViewController;
- (void)presentPopUpViewController:(UIViewController *)popupViewController completion:(void (^)(void))completionBlock;
- (void)dismissPopUpViewController;
- (void)dismissPopUpViewControllerWithcompletion:(void (^)(void))completionBlock;
//- (IBAction)disMe:(id)sender;
#end
This is my popup method declared in `#import "UIViewController+ENPopUp.m"`
#import "UIViewController+ENPopUp.h"
#import "JWBlurView.h"
#import <objc/runtime.h>
static void * ENPopupViewControllerPropertyKey = &ENPopupViewControllerPropertyKey;
static CGFloat const kAnimationDuration = .4f;
static CGFloat const kRotationAngle = 70.f;
static NSInteger const kENPopUpOverlayViewTag = 351301;
static NSInteger const kENPopUpViewTag = 351302;
static NSInteger const kENPopUpBluredViewTag = 351303;
#implementation UIViewController (ENPopUp)
#pragma mark - Public Methods
- (void)presentPopUpViewController:(UIViewController *)popupViewController
{
[self presentPopUpViewController:popupViewController completion:nil];
}
- (void)presentPopUpViewController:(UIViewController *)popupViewController completion:(void (^)(void))completionBlock
{
self.en_popupViewController = popupViewController;
[self presentPopUpView:popupViewController.view completion:completionBlock];
}
- (void)dismissPopUpViewController
{
[self dismissPopUpViewControllerWithcompletion:nil];
}
- (void)dismissPopUpViewControllerWithcompletion:(void (^)(void))completionBlock
{
UIView *sourceView = [self topView];
JWBlurView *blurView = (JWBlurView *)[sourceView viewWithTag:kENPopUpBluredViewTag];
UIView *popupView = [sourceView viewWithTag:kENPopUpViewTag];
UIView *overlayView = [sourceView viewWithTag:kENPopUpOverlayViewTag];
[self performDismissAnimationInSourceView:sourceView withBlurView:blurView popupView:popupView overlayView:overlayView completion:completionBlock];
}
//- (IBAction)disMe:(id)sender {
// UIViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"Pop"];
// [self dismissViewControllerAnimated:YES completion:Nil];
//}
#pragma mark - Getters & Setters
- (UIViewController *)en_popupViewController
{
return objc_getAssociatedObject(self, ENPopupViewControllerPropertyKey);
}
- (void)setEn_popupViewController:(UIViewController *)en_popupViewController
{
objc_setAssociatedObject(self, ENPopupViewControllerPropertyKey, en_popupViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - View Handling
- (void)presentPopUpView:(UIView *)popUpView completion:(void (^)(void))completionBlock
{
UIView *sourceView = [self topView];
// Check if source view controller is not in destination
if ([sourceView.subviews containsObject:popUpView]) return;
// Add overlay
UIView *overlayView = [[UIView alloc] initWithFrame:sourceView.bounds];
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
overlayView.tag = kENPopUpOverlayViewTag;
overlayView.backgroundColor = [UIColor clearColor];
// Add Blured View
JWBlurView *bluredView = [[JWBlurView alloc] initWithFrame:overlayView.bounds];
bluredView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
bluredView.tag = kENPopUpBluredViewTag;
[bluredView setBlurAlpha:.0f];
[bluredView setAlpha:.0f];
[bluredView setBlurColor:[UIColor clearColor]];
bluredView.backgroundColor = [UIColor clearColor];
[overlayView addSubview:bluredView];
// Make the background clickable
UIButton * dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
dismissButton.backgroundColor = [UIColor clearColor];
dismissButton.frame = sourceView.bounds;
[overlayView addSubview:dismissButton];
[dismissButton addTarget:self action:#selector(dismissPopUpViewController)
forControlEvents:UIControlEventTouchUpInside];
// Customize popUpView
popUpView.layer.cornerRadius = 3.5f;
popUpView.layer.masksToBounds = YES;
popUpView.layer.zPosition = 99;
popUpView.tag = kENPopUpViewTag;
popUpView.center = overlayView.center;
[popUpView setNeedsLayout];
[popUpView setNeedsDisplay];
[overlayView addSubview:popUpView];
[sourceView addSubview:overlayView];
[self setAnimationStateFrom:popUpView];
[self performAppearAnimationWithBlurView:bluredView popupView:popUpView completion:completionBlock];
}
#pragma mark - Animation
- (void)setAnimationStateFrom:(UIView *)view
{
CALayer *layer = view.layer;
layer.transform = [self transform3d];
}
- (CATransform3D)transform3d
{
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DTranslate(transform, 0, 200.f, 0);
transform.m34 = 1.0/800.0;
transform = CATransform3DRotate(transform, kRotationAngle*M_PI/180.f, 1.f, .0f, .0f);
CATransform3D scale = CATransform3DMakeScale(.7f, .7f, .7f);
return CATransform3DConcat(transform, scale);
}
- (void)performAppearAnimationWithBlurView:(JWBlurView *)blurView popupView:(UIView *)popupView completion:(void (^)(void))completionBlock
{
CATransform3D transform;
transform = CATransform3DIdentity;
[UIView animateWithDuration:kAnimationDuration
animations:^ {
[self.en_popupViewController viewWillAppear:NO];
[blurView setAlpha:1.f];
popupView.layer.transform = transform;
}
completion:^(BOOL finished) {
[self.en_popupViewController viewDidAppear:NO];
if (completionBlock != nil) {
completionBlock();
}
}];
}
- (void)performDismissAnimationInSourceView:(UIView *)sourceView
withBlurView:(JWBlurView *)blurView
popupView:(UIView *)popupView
overlayView:(UIView *)overlayView
completion:(void (^)(void))completionBlock
{
CATransform3D transform = [self transform3d];
[UIView animateWithDuration:kAnimationDuration
animations:^ {
[self.en_popupViewController viewWillDisappear:NO];
[blurView setAlpha:0.f];
popupView.layer.transform = transform;
}
completion:^(BOOL finished) {
[popupView removeFromSuperview];
[blurView removeFromSuperview];
[overlayView removeFromSuperview];
[self.en_popupViewController viewDidDisappear:NO];
self.en_popupViewController = nil;
if (completionBlock != nil) {
completionBlock();
}
}];
}
#pragma mark - Getters
- (UIView*)topView {
UIViewController *recentView = self;
while (recentView.parentViewController != nil) {
recentView = recentView.parentViewController;
}
return recentView.view;
}
#end
I am beginner in ios.Some one please give some solution for my problem.Thanks in advance !
I changed the code of UIViewController+ENPopUp.m for have a visible button.
In code existe a button with screen size for dismiss view. I changed this button and now he is visible.
In presentPopUpView UIViewController+ENPopUp.m where have this comment " // Make the background clickable" I change the code to this:
CGFloat buttonSize = 10;
UIButton * dismissButton = [[UIButton alloc] init];
dismissButton.backgroundColor = [UIColor redColor];
dismissButton.frame = CGRectMake((self.view.frame.size.width/2) - (buttonSize/2),(self.view.frame.size.height/2) + (self.view.frame.size.height/4) , buttonSize, buttonSize);
[overlayView addSubview:dismissButton];
[dismissButton addTarget:self action:#selector(dismissPopUpViewController)
forControlEvents:UIControlEventTouchUpInside];
This create a red button 10x10 like in figure:
Now, you can change the color and the size of button for what you want.
I hope this can help you.
I've made a subview to record sound which looks like this:
http://i.stack.imgur.com/EBdEV.png
After the bars faded, it looks like this:
http://i.imgur.com/sDAp4nk.png
I couldn't figure out why. This is the controller. Is there something in the storyboard I could turn on/off?
Thanks for help :)
#import "SubviewController.h"
#implementation SubviewController
#synthesize delegate;
- (void)viewWillAppear:(BOOL)inAnimated {
[super viewWillAppear:inAnimated];
if([self.delegate respondsToSelector:#selector(subviewControllerWillAppear:)]) {
[self.delegate subviewControllerWillAppear:self];
}
}
- (void)viewWillDisappear:(BOOL)inAnimated {
if([self.delegate respondsToSelector:#selector(subviewControllerWillDisappear:)]) {
[self.delegate subviewControllerWillDisappear:self];
}
[super viewWillDisappear:inAnimated];
}
- (void)presentFromViewController:(UIViewController *)inViewController animated:(BOOL)inAnimated {
CGRect theBounds = inViewController.view.bounds;
UIView *theView = self.view;
UIView *theBackgroundView = [[UIView alloc] initWithFrame:theBounds];
CGRect theFrame = theView.frame;
theFrame.origin.x = (CGRectGetWidth(theBounds) - CGRectGetWidth(theFrame)) / 2.0;
theFrame.origin.y = (CGRectGetHeight(theBounds) - CGRectGetHeight(theFrame)) / 2.0;
theView.frame = theFrame;
[inViewController addChildViewController:self];
theBackgroundView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
theBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theBackgroundView.alpha = 0.0;
[theBackgroundView addSubview:theView];
[inViewController.view addSubview:theBackgroundView];
[UIView animateWithDuration:inAnimated ? 0.75 : 0.0
animations:^{
theBackgroundView.alpha = 1.0;
}
completion:^(BOOL inFinished) {
[self didMoveToParentViewController:inViewController];
}];
}
- (void)dismissAnimated:(BOOL)inAnimated {
UIView *theView = self.view;
UIView *theBackgroundView = theView.superview;
[self willMoveToParentViewController:nil];
[UIView animateWithDuration:inAnimated ? 0.75 : 0.0
animations:^{
theBackgroundView.alpha = 0.0;
}
completion:^(BOOL inFinished) {
[theBackgroundView removeFromSuperview];
[theView removeFromSuperview];
[self removeFromParentViewController];
}];
}
#end
#implementation SubviewSegue
- (void)perform {
[self.destinationViewController presentFromViewController:self.sourceViewController animated:YES];
}
#end
Check your constraints. You probably have one that has a minimum height between 2 subviews and its pushing that toolbar down or something.
Also, a lot of weird things happen when you animate frames while autolayout is on so be careful about that.
In my app I've to create a custom alert view like the following:
So I followed this tutorial to create a custom alert view. I finished it but I'm getting issue in the following method:
- (void)addOrRemoveButtonWithTag:(int)tag andActionToPerform:(BOOL)shouldRemove {
NSMutableArray *items = [[NSMutableArray alloc]init];
[items addObject:self.buttonOk];
[items addObject:self.buttonClose];
int buttonIndex = (tag == 1);
if (shouldRemove) {
[items removeObjectAtIndex:buttonIndex];
} else {
if (tag == 1) {
[items insertObject:self.buttonOk atIndex:buttonIndex];
} else {
[items insertObject:self.buttonClose atIndex:buttonIndex];
}
}
}
I edited it than the tutorial because I don't need a UIToolBar for buttons. When I run the app it says me that I can't insert a nil object in an NSMutableArray, but I don't understand what's wrong, I hope you can help me to fix this issue.
UPDATE
Here's all the class code I developed:
#import "CustomAlertViewController.h"
#define ANIMATION_DURATION 0.25
#interface CustomAlertViewController ()
- (IBAction)buttonOk:(UIButton *)sender;
- (IBAction)buttonCancel:(UIButton *)sender;
#property (weak, nonatomic) IBOutlet UIButton *buttonClose;
#property (weak, nonatomic) IBOutlet UIButton *buttonOk;
#property (strong, nonatomic) IBOutlet UIView *viewAlert;
-(void)addOrRemoveButtonWithTag:(int)tag andActionToPerform:(BOOL)shouldRemove;
#end
#implementation CustomAlertViewController
- (id)init
{
self = [super init];
if (self) {
[self.viewAlert setFrame:CGRectMake(self.labelAlertView.frame.origin.x,
self.labelAlertView.frame.origin.y,
self.labelAlertView.frame.size.width,
self.viewAlert.frame.size.height)];
[self.buttonOk setTag:1];
[self.buttonClose setTag:0];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)showCustomAlertInView:(UIView *)targetView withMessage:(NSString *)message {
CGFloat statusBarOffset;
if (![[UIApplication sharedApplication] isStatusBarHidden]) {
CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
if (statusBarSize.width < statusBarSize.height) {
statusBarOffset = statusBarSize.width;
} else {
statusBarOffset = statusBarSize.height;
}
} else {
statusBarOffset = 0.0;
}
CGFloat width, height, offsetX, offsetY;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft ||
[[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
width = targetView.frame.size.width;
height = targetView.frame.size.height;
offsetX = 0.0;
offsetY = -statusBarOffset;
}
[self.view setFrame:CGRectMake(targetView.frame.origin.x, targetView.frame.origin.y, width, height)];
[self.view setFrame:CGRectOffset(self.view.frame, offsetX, offsetY)];
[targetView addSubview:self.view];
[self.viewAlert setFrame:CGRectMake(0.0, -self.viewAlert.frame.size.height, self.viewAlert.frame.size.width, self.viewAlert.frame.size.height)];
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationDuration:ANIMATION_DURATION];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self.viewAlert setFrame:CGRectMake(0.0, 0.0, self.viewAlert.frame.size.width, self.viewAlert.frame.size.height)];
[UIView commitAnimations];
[self.labelAlertView setText:#"CIAO"];
}
- (void)removeCustomAlertFromView {
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationDuration:ANIMATION_DURATION];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self.viewAlert setFrame:CGRectMake(0.0, -self.viewAlert.frame.size.height, self.viewAlert.frame.size.width, self.viewAlert.frame.size.height)];
[UIView commitAnimations];
[self.view performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:ANIMATION_DURATION];
}
- (void)removeCustomAlertFromViewInstantly {
[self.view removeFromSuperview];
}
- (BOOL)isOkayButtonRemoved {
if (self.buttonOk == nil) {
return YES;
} else {
return NO;
}
}
- (BOOL)isCancelButtonRemoved {
if (self.buttonClose == nil) {
return YES;
} else {
return NO;
}
}
- (void)removeOkayButton:(BOOL)shouldRemove {
if ([self isOkayButtonRemoved] != shouldRemove) {
[self addOrRemoveButtonWithTag:1 andActionToPerform:shouldRemove];
}
}
- (void)removeCancelButton:(BOOL)shouldRemove {
if ([self isCancelButtonRemoved] != shouldRemove) {
[self addOrRemoveButtonWithTag:0 andActionToPerform:shouldRemove];
}
}
- (void)addOrRemoveButtonWithTag:(int)tag andActionToPerform:(BOOL)shouldRemove {
NSMutableArray *items = [[NSMutableArray alloc]init];
[items addObject:self.buttonOk];
[items addObject:self.buttonClose];
int buttonIndex = (tag == 1);
if (shouldRemove) {
[items removeObjectAtIndex:buttonIndex];
} else {
if (tag == 1) {
[items insertObject:self.buttonOk atIndex:buttonIndex];
} else {
[items insertObject:self.buttonClose atIndex:buttonIndex];
}
}
}
- (IBAction)buttonOk:(UIButton *)sender {
[self.delegate customAlertOk];
}
- (IBAction)buttonCancel:(UIButton *)sender {
[self.delegate customAlertCancel];
}
#end
UPDATE 2
Code in which I use the CustomAlertView:
#import "PromotionsViewController.h"
#import "CustomAlertViewController.h"
#interface PromotionsViewController () <CustomAlertViewControllerDelegate> {
BOOL isDeletingItem;
}
#property(nonatomic,strong) CustomAlertViewController *customAlert;
- (IBAction)buttonBack:(UIButton *)sender;
#property (weak, nonatomic) IBOutlet UIButton *buttonAlert;
- (IBAction)buttonAlert:(UIButton *)sender;
#end
#implementation PromotionsViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.buttonAlert setTitle:self.promotionSelected forState:UIControlStateNormal];
[self.customAlert setDelegate:self];
isDeletingItem = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)buttonBack:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [[CustomAlertViewController alloc]init];
[self.customAlert removeOkayButton:NO];
[self.customAlert removeCancelButton:NO];
NSString *message = [NSString stringWithFormat:#"La tua offerta %# del 20%% è stata convertita in punti IoSi x10", self.promotionSelected];
[self.customAlert showCustomAlertInView:self.view withMessage:message];
isDeletingItem = YES;
}
- (void)customAlertOk {
if (isDeletingItem) {
[self.customAlert removeCustomAlertFromViewInstantly];
} else {
[self.customAlert removeCustomAlertFromView];
}
}
- (void)customAlertCancel {
[self.customAlert removeCustomAlertFromView];
if (isDeletingItem) {
isDeletingItem = NO;
}
}
#end
Maybe you're calling addOrRemoveButtonWithTag:andActionToPerform: at a time where your UI is not fully created, since UI elements are created asynchronously. So if you call this method, right after custom alert view instanciation, you'll get your crash because the buttons in the view are not created.
To solve this issue, you need to call addOrRemoveButtonWithTag:andActionToPerform: only once your custom alert has been added to the view hierarchy.
EDIT :
With the example code you gave in edit 2, you call these lines :
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [[CustomAlertViewController alloc]init];
[self.customAlert removeOkayButton:NO];
[self.customAlert removeCancelButton:NO];
}
but when you have just instantiated CustomAlertViewController, its 2 buttons are not yet created, so I suggest you add 2 properties hasOkButton and hasCancelButton and a new constructor to your custom class like this one :
- (instancetype) initWithOk:(BOOL)OkButton AndCancel:(BOOL) CancelButton
{
if(self = [super init])
{
hasOkButton = OkButton;
hasCancelButton = CancelButton;
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// At this time, the custom UI buttons will be created in the UI view hierarchy
[self removeOkayButton: hasOkButton];
[self removeOkayButton: hasCancelButton];
}
And in the caller you can use the following to display a custom alert View:
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [[CustomAlertViewController alloc] initWithOk:NO AndCancel:NO];
// ...
}
EDIT #2
I tried your solution in a real project, I made it work by using these lines int the caller :
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [self.storyboard instantiateViewControllerWithIdentifier:#"customAlertView"];
self.customAlert.hasOK = NO;
self.customAlert.hasCancel = YES;
NSString *message = [NSString stringWithFormat:#"La tua offerta %# del 20%% è stata convertita in punti IoSi x10", self.promotionSelected];
[self.customAlert showCustomAlertInView:self.view withMessage:message];
isDeletingItem = YES;
}
In the CustomAlertViewController declare 2 visible properties hasOK and hasCancel in.h.
And modify your .m by adding method :
-(void)viewWillAppear:(BOOL)animated
{
[self removeOkayButton:self.hasOK];
[self removeCancelButton:self.hasCancel];
}
Be sure to modify your storyboard (if eligible) to have the "customAlertView" defined this way :
Don't forget also to bind your UIButton to the controller this can be a mistake too in your implementation :
Hope this will help you :)
I found on the web a tutorial to create custom alert view by using code, if you are interested you can go to this tutorial. I used it for my issue and it worked great! You have to fix a few things, because it uses deprecated command but it's easy to fix it.
If you are interested just take a look about this tutorial. I think you can integrate it in your app and after you can easily use for other stuff if it's necessary. I hope that my answer will help someone.
I have created a SlidingViewController which has a Main, Right, and Left view controllers. Now I want to add a new function/method that is called whenever one of the RightViewController's table view cell is clicked to switch the main view controller to what ever view controller is linked with that table view cell at indexpath.row. The new view controller should still be able to have this right and left view controller available. Anyone have any experience creating their own SlidingViewController or MenuViewController that can help out on how I should approach this? Any help will be gratefully appreciated. Thank you in advance.
SlidingViewController.h
#interface SlidingViewController : UIViewController <UIGestureRecognizerDelegate>
#property (retain) UIViewController *mainViewController;
#property (retain) UIViewController *leftViewController;
#property (retain) UIViewController *rightViewController;
#property (retain) NSNumber *leftSwipeEnabled;
#property (assign) BOOL leftDrawerVisible;
#property (retain) NSNumber *rightSwipeEnabled;
#property (assign) BOOL rightDrawerVisible;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil mainViewController:(UIViewController *)main leftViewController:(UIViewController *)left andRightViewController:(UIViewController *)right;
-(void)toggleLeftDrawer;
-(void)toggleRightDrawer;
#end
SlidingViewController.m
#implementation SlidingViewController
#synthesize mainViewController, leftViewController, rightViewController;
#synthesize leftDrawerVisible, rightDrawerVisible;
#synthesize leftSwipeEnabled, rightSwipeEnabled;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil mainViewController:(UIViewController *)main leftViewController:(UIViewController *)left andRightViewController:(UIViewController *)right
{
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
{
mainViewController = main;
leftViewController = left;
rightViewController = right;
leftSwipeEnabled = [NSNumber numberWithBool:NO];
rightSwipeEnabled = [NSNumber numberWithBool:NO];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self addChildViewController:self.mainViewController];
[self.view addSubview:[self.mainViewController view]];
self.mainViewController.view.frame = self.view.frame;
[self.mainViewController didMoveToParentViewController:self];
self.mainViewController.view.layer.shadowColor = [UIColor blackColor].CGColor;
self.mainViewController.view.layer.shadowOpacity = 0.2f;
self.mainViewController.view.layer.shadowRadius = 5.0f;
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
if(self.leftViewController != nil)
{
UISwipeGestureRecognizer *leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
leftSwipeRecognizer.delegate = self;
[self.mainViewController.view addGestureRecognizer:leftSwipeRecognizer];
}
if(self.rightViewController != nil)
{
UISwipeGestureRecognizer *rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
rightSwipeRecognizer.delegate = self;
[self.mainViewController.view addGestureRecognizer:rightSwipeRecognizer];
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self layoutShadowWithDuration:0];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if(self.leftDrawerVisible)
[self toggleLeftDrawer];
else if(self.rightDrawerVisible)
[self toggleRightDrawer];
}
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
-(void)handleSwipe:(UISwipeGestureRecognizer *)recognizer
{
if(recognizer.state == UIGestureRecognizerStateEnded)
{
if(recognizer.direction == UISwipeGestureRecognizerDirectionRight && [self.leftSwipeEnabled boolValue])
[self toggleLeftDrawer];
else if(recognizer.direction == UISwipeGestureRecognizerDirectionLeft && [self.rightSwipeEnabled boolValue])
[self toggleRightDrawer];
}
}
-(void)toggleLeftDrawer
{
if (self.rightDrawerVisible)
{
return;
}
if(self.leftDrawerVisible)
{
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mainViewController.view.frame = CGRectMake(0.0, 0.0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height);}
completion:^(BOOL finished)
{
[self.leftViewController.view removeFromSuperview];
[self.leftViewController willMoveToParentViewController:nil];
[self.leftViewController removeFromParentViewController];
}];
self.leftDrawerVisible = NO;
self.mainViewController.view.userInteractionEnabled = YES;
}
else
{
[self addChildViewController:self.leftViewController];
[self.view insertSubview:[self.leftViewController view] belowSubview:[self.mainViewController view]];
[self.leftViewController didMoveToParentViewController:self];
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
self.mainViewController.view.layer.shadowOffset = CGSizeMake(-3, 0);
NSInteger width = 260;
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
width = 320;
self.leftViewController.view.frame = CGRectMake(0, 0, width, self.view.bounds.size.height);
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{ self.mainViewController.view.frame = CGRectMake(width, 0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height); }
completion:^(BOOL finished) { self.leftViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin; }];
self.leftDrawerVisible = YES;
self.mainViewController.view.userInteractionEnabled = NO;
}
}
-(void)toggleRightDrawer
{
if(self.leftDrawerVisible)
return;
if(self.rightDrawerVisible)
{
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mainViewController.view.frame = CGRectMake(0.0, 0.0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height);
}
completion:^(BOOL finished){
[self.rightViewController.view removeFromSuperview];
[self.rightViewController willMoveToParentViewController:nil];
[self.rightViewController removeFromParentViewController];
}];
self.rightDrawerVisible = NO;
self.mainViewController.view.userInteractionEnabled = YES;
}
else
{
[self addChildViewController:self.rightViewController];
[self.view insertSubview:[self.rightViewController view] belowSubview:[self.mainViewController view]];
[self.rightViewController didMoveToParentViewController:self];
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
self.mainViewController.view.layer.shadowOffset = CGSizeMake(3, 0);
NSInteger width = 260;
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
width = 320;
self.rightViewController.view.frame = CGRectMake(self.view.bounds.size.width- width, 0, width, self.view.bounds.size.height);
[UIView animateWithDuration:0.2 delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mainViewController.view.frame = CGRectMake(-width, 0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height);
}
completion:^(BOOL finished){
self.rightViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin;
}];
self.rightDrawerVisible = YES;
self.mainViewController.view.userInteractionEnabled = NO;
}
}
-(void) layoutShadowWithDuration:(NSTimeInterval)duration
{
CGPathRef oldShadowPath = self.mainViewController.view.layer.shadowPath;
if (oldShadowPath)
{
CFRetain(oldShadowPath);
}
// Update shadow path for the view
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
// You would think setting duration to 0 would cause the animation added below to not animate. You would be wrong.
if (duration != 0)
{
if (oldShadowPath)
{
[self.mainViewController.view.layer addAnimation:((^ {
CABasicAnimation *transition = [CABasicAnimation animationWithKeyPath:#"shadowPath"];
transition.fromValue = (__bridge id)oldShadowPath;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.duration = duration;
return transition;
})()) forKey:#"transition"];
CFRelease(oldShadowPath);
}
else
if (oldShadowPath)
CFRelease(oldShadowPath);
}
else
if (oldShadowPath)
CFRelease(oldShadowPath);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self layoutShadowWithDuration:duration];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
You can simply do this by "Delegation Design Pattern"
In RightViewController.h add the following lines.
#protocol RightViewControllerDelegate <NSObject>
#required
- (void)somethingClicked : (id)theObject;
#end
#interface RightViewController : UIViewController
#property (nonatomic, assign) id<RightViewControllerDelegate> delegate;
#end
In RightViewController.m add the following lines.
- (void)somethingClicked : (id)theObject
{
[_delegate somethingClicked:theObject];
}
Update MainViewController.h with the following code.
#interface MainViewController : UIViewController <RightViewControllerDelegate>
And Finally add the following code in the MainViewController.m
- (void)somethingClicked : (id)theObject
{
// Perform Your Task
}