i got weird result on my app. when i pressed back button its always call scrollViewDidScroll and it makes my app crash.
error not happen if i'm not scrolling to the bottom (just load the view and then pressed back button).
but when i scrolled to the bottom and then press back button it force quit.
also no error if i'm scrolling to the bottom then scroll again back to the middle of tableview.
here is my code snippet.
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Securities Instructions";
currentPage = 1;
isReloading = NO;
totalPages = 1;
isScrollingEnable = YES;
...... bla bla bla....
[self.tableView reloadData];
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 90, 0); //values passed are - top, left, bottom, right
}
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// back button was pressed. We know this is true because self is no longer
// in the navigation stack.
NSLog(#"back button pressed");
isScrollingEnable = NO;
}
// [super viewWillDisappear:animated];
}
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
NSLog(#"scroll view called");
if (isScrollingEnable) {
NSLog(#"scroll view get called: scroll enalbe");
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
float reload_distance = 20;
if(y > h + reload_distance) {
if (isReloading==NO) {
if (totalPages>1) {
if (currentPage<totalPages) {
NSLog(#"scroll view called");
currentPage++;
isReloading = YES;
[self getJsonOnLoad];
}
}
}
}
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
NSLog(#"index:%d",buttonIndex);
if (buttonIndex==0) {
if (totalPages!=9999) {
[self.navigationController popViewControllerAnimated:YES];
}
}
}
- (void) getTotalPages{
NSArray *detailArray = [self.jsonResult objectForKey:#"detail"];
int detailCount = [detailArray count];
//server tidak konsisten,parameter totalpages di menu ini tidak ada (menu lain ada) -_-!
if (detailCount>=25) {
if ([jsonHeader objectForKey:#"totalRows"]) {
totalPages = [[jsonHeader objectForKey:#"totalRows"] floatValue]/25;
}else{
//buat unlimited
totalPages = 9999;
}
}
}
//orientation handling
- (void)canRotate{}
- (void)viewWillAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
-(void)viewDidDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)orientationChanged:(NSNotification *)notification{
NSLog(#"orientation change...");
[self adjustViewsForOrientation:[[UIDevice currentDevice] orientation]];
}
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
NSLog(#"orientation:%d",orientation);
if (orientation == UIInterfaceOrientationPortrait)
// if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
landscape = NO;
//harus di tambahin ini karena parentnya uiviewcontroller bukan uitableviewcontroller jadi tidak otomatis
self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y, 320, 480);
[self.tableView layoutSubviews];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
[self.tableView reloadData];
//load the portrait view
NSLog(#"portraid");
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.navigationController.navigationBar setHidden:NO];
}
// else if (orientation == UIInterfaceOrientationLandscapeLeft)
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
landscape = YES;
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
//harus di tambahin ini karena parentnya uiviewcontroller bukan uitableviewcontroller jadi tidak otomatis
self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y, 480, 320);
[self.tableView layoutSubviews];
[self.tableView reloadData];
//load the landscape view
NSLog(#"landscape");
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.navigationController.navigationBar setHidden:NO];
}
}
in .h
#import <UIKit/UIKit.h>
#interface SecuritiesInstructionsResultViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>{
NSDictionary *jsonHeader;
//untuk paging
NSInteger currentPage;
Boolean *isReloading;
NSInteger totalRows;
float totalPages;
BOOL landscape;
BOOL isScrollingEnable;
}
#property (strong, nonatomic) IBOutlet UITableView *tableView;
//#property NSMutableArray *rowData;
#property NSMutableArray *dataAccount;
#property NSMutableArray *dataCashAmount;
#property NSMutableArray *dataCode;
#property NSMutableArray *dataDate;
#property NSMutableArray *dataExtRef;
#property NSMutableArray *dataInsType;
#property NSMutableArray *dataSecuritiesAmmount;
#property NSMutableArray *dataStatus;
#property NSString *dateFrom;
#property NSString *dateTo;
#property NSDictionary *jsonResult;
#property NSString *selectedAccount;
#property NSString *selectedSecurityCode;
#property NSString *selectedSecuritySecCodeType;
#property NSString *selectedSecurityType;
#property NSString *selectedCurrencyCode;
#property NSString *selectedStatus;
#property NSString *selectedDate;
#property NSString *selectedToDate;
#end
i've prevent to not called scrollviewdidscroll method when back button pressed by implementing viewWillDisappear, but it doesn't help. the app is still crash and no error message.
Any one have a clue for this problem?
Set delegate of UIScrollView or inherited classes (UITableView, UICollectionView and etc) to nil
Objective-C:
- (void)dealloc {
[scrollView setDelegate:nil];
}
Swift:
deinit {
scrollView.delegate = nil
}
Related
I am using IQKeyboardManagerSwift in my app. It is working perfectly in some ViewControllers I have to disable it's toolbar. According to documentation I should add viewcontrollers in which I want to disable toolbar in following array.
IQKeyboardManager.sharedManager().disabledToolbarClasses = []
Now It is working perfectly for TextFields in view controller. But problem is with TextFields inside webview. I have a viewcontroller in which I have to input data in fields inside WebView, for fields inside webview Toolbar is not disabled I tried many ways. Even disabling IQKeyBaord (IQKeyboardManager.sharedManager().enable = false) for whole app doesn't effect WebView
I do not understand what is the issue is there some specific handling for WebView or what can be possible alternate.
For clarification top view with Done button is toolbar that I am referring.
Toolbar over the keyboard is the default property/behaviour of WebView/WKWebView.
But if you want to remove it, here is the code, you can modify it according to your need :
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIWebView *webView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(removeKeyboardTopBar:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(removeKeyboardTopBar:) name:UIKeyboardDidShowNotification object:nil];
}
-(void)viewDidAppear:(BOOL)animated{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://html5doctor.com/demos/forms/forms-example.html"]];
[_webView loadRequest:request];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) removeKeyboardTopBar:(NSNotification*)notify{
// Locate non-UIWindow.
UIWindow *keyboardWindow = nil;
__block UIView* toolBarContainer = nil;
NSArray* windows = [[UIApplication sharedApplication] windows];
for (UIWindow *possibleWindow in windows) {
if (![[possibleWindow class] isEqual:[UIWindow class]]) {
keyboardWindow = possibleWindow;
break;
}
}
for (UIView *possibleFormView in [keyboardWindow subviews])
{
if([[[UIDevice currentDevice] systemVersion] floatValue]>8){
if([[possibleFormView description] hasPrefix:#"<UIInputSetContainerView"])
{
for(int i = 0 ; i < [possibleFormView.subviews count] ; i++)
{
UIView* hostkeyboard = [possibleFormView.subviews objectAtIndex:i];
if([[hostkeyboard description] hasPrefix:#"<UIInputSetHostView"])
{
for (id temp in hostkeyboard.subviews)
{
if ([[temp description] hasPrefix:#"<UIWebFormAccessory"])
{
UIView* currentToolBar = (UIView*)temp;
currentToolBar.hidden = true;
toolBarContainer = hostkeyboard;
}
}
}
}
}
}else{
if ([[possibleFormView description] rangeOfString:#"UIPeripheralHostView"].location != NSNotFound) {
for (UIView *subviewWhichIsPossibleFormView in [possibleFormView subviews]) {
if ([[subviewWhichIsPossibleFormView description] rangeOfString:#"UIWebFormAccessory"].location != NSNotFound) {
[subviewWhichIsPossibleFormView removeFromSuperview];
}
}
}
}
}
if(toolBarContainer){
if([notify.name isEqualToString:#"UIKeyboardWillShowNotification"]){
[toolBarContainer setHidden:YES];
}else if([notify.name isEqualToString:#"UIKeyboardDidShowNotification"]){
[toolBarContainer setHidden:NO];
}
dispatch_async(dispatch_get_main_queue(), ^(){
toolBarContainer.frame = CGRectMake(toolBarContainer.frame.origin.x,toolBarContainer.frame.origin.y+44,toolBarContainer.frame.size.width,toolBarContainer.frame.size.height);
});
}
keyboardWindow = nil;
}
I recently downloaded code for creating a calendar in xCode using objective-c. The code works for the most part, it highlights the days a user selects, but it is supposed to log out the start and end dates the user has selected. A delegate is used for this between my view controller and uiview (calendar view), but when my delegate is set in viewDidLoad, I noticed that it is always set to null. I am new to app making so would appreciate any help.
viewcontroller.m:
#import "CalendarViewController.h"
#import "DSLCalendarView.h"
#interface CalendarViewController ()<DSLCalendarViewDelegate>
#property (nonatomic, weak) IBOutlet DSLCalendarView *calendarView;
#property (weak, nonatomic) IBOutlet UIButton *selectDatesButton;
#end
#implementation CalendarViewController
- (void)viewDidLoad;
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.calendarView.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark - DSLCalendarViewDelegate methods
- (void)calendarView:(DSLCalendarView *)calendarView didSelectRange:(DSLCalendarRange *)range;
{
if (range != nil) {
NSLog( #"Selected %ld/%ld - %ld/%ld", (long)range.startDay.day, (long)range.startDay.month, (long)range.endDay.day, (long)range.endDay.month);
}
else {
NSLog( #"No selection" );
}
}
calendarView.m (not all code, just methods using the delegate)
#import "DSLCalendarDayCalloutView.h"
#import "DSLCalendarDayView.h"
#import "DSLCalendarMonthSelectorView.h"
#import "DSLCalendarMonthView.h"
#import "DSLCalendarView.h"
#import "DSLCalendarDayView.h"
#interface DSLCalendarView ()
#property (nonatomic, strong) DSLCalendarDayCalloutView *dayCalloutView;
#property (nonatomic, copy) NSDateComponents *draggingFixedDay;
#property (nonatomic, copy) NSDateComponents *draggingStartDay;
#property (nonatomic, assign) BOOL draggedOffStartDay;
#property (nonatomic, strong) NSMutableDictionary *monthViews;
#property (nonatomic, strong) UIView *monthContainerView;
#property (nonatomic, strong) UIView *monthContainerViewContentView;
#property (nonatomic, strong) DSLCalendarMonthSelectorView *monthSelectorView;
#end
#implementation DSLCalendarView {
CGFloat _dayViewHeight;
NSDateComponents *_visibleMonth;
}
- (void)positionViewsForMonth:(NSDateComponents*)month fromMonth:(NSDateComponents*)fromMonth animated:(BOOL)animated;
{
fromMonth = [fromMonth copy];
month = [month copy];
CGFloat nextVerticalPosition = 0;
CGFloat startingVerticalPostion = 0;
CGFloat restingVerticalPosition = 0;
CGFloat restingHeight = 0;
NSComparisonResult monthComparisonResult = [month.date compare:fromMonth.date];
NSTimeInterval animationDuration = (monthComparisonResult == NSOrderedSame || !animated) ? 0.0 : 0.5;
NSMutableArray *activeMonthViews = [[NSMutableArray alloc] init];
// Create and position the month views for the target month and those around it
for (NSInteger monthOffset = -2; monthOffset <= 2; monthOffset += 1)
{
NSDateComponents *offsetMonth = [month copy];
offsetMonth.month = offsetMonth.month + monthOffset;
offsetMonth = [offsetMonth.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitCalendar fromDate:offsetMonth.date];
// Check if this month should overlap the previous month
if (![self monthStartsOnFirstDayOfWeek:offsetMonth]) {
nextVerticalPosition -= _dayViewHeight;
}
// Create and position the month view
DSLCalendarMonthView *monthView = [self cachedOrCreatedMonthViewForMonth:offsetMonth];
[activeMonthViews addObject:monthView];
[monthView.superview bringSubviewToFront:monthView];
CGRect frame = monthView.frame;
frame.origin.y = nextVerticalPosition;
nextVerticalPosition += frame.size.height;
monthView.frame = frame;
// Check if this view is where we should animate to or from
if (monthOffset == 0) {
// This is the target month so we can use it to determine where to scroll to
restingVerticalPosition = monthView.frame.origin.y;
restingHeight += monthView.bounds.size.height;
}
else if (monthOffset == 1 && monthComparisonResult == NSOrderedAscending) {
// This is the month we're scrolling back from
startingVerticalPostion = monthView.frame.origin.y;
if ([self monthStartsOnFirstDayOfWeek:offsetMonth]) {
startingVerticalPostion -= _dayViewHeight;
}
}
else if (monthOffset == -1 && monthComparisonResult == NSOrderedDescending) {
// This is the month we're scrolling forward from
startingVerticalPostion = monthView.frame.origin.y;
if ([self monthStartsOnFirstDayOfWeek:offsetMonth]) {
startingVerticalPostion -= _dayViewHeight;
}
}
// Check if the active or following month start on the first day of the week
if (monthOffset == 0 && [self monthStartsOnFirstDayOfWeek:offsetMonth]) {
// If the active month starts on a monday, add a day view height to the resting height and move the resting position up so the user can drag into that previous month
restingVerticalPosition -= _dayViewHeight;
restingHeight += _dayViewHeight;
}
else if (monthOffset == 1 && [self monthStartsOnFirstDayOfWeek:offsetMonth]) {
// If the month after the target month starts on a monday, add a day view height to the resting height so the user can drag into that month
restingHeight += _dayViewHeight;
}
}
// Size the month container to fit all the month views
CGRect frame = self.monthContainerViewContentView.frame;
frame.size.height = CGRectGetMaxY([[activeMonthViews lastObject] frame]);
self.monthContainerViewContentView.frame = frame;
// Remove any old month views we don't need anymore
NSArray *monthViewKeyes = self.monthViews.allKeys;
for (NSString *key in monthViewKeyes) {
UIView *monthView = [self.monthViews objectForKey:key];
if (![activeMonthViews containsObject:monthView]) {
[monthView removeFromSuperview];
[self.monthViews removeObjectForKey:key];
}
}
// Position the content view to show where we're animating from
if (monthComparisonResult != NSOrderedSame) {
CGRect frame = self.monthContainerViewContentView.frame;
frame.origin.y = -startingVerticalPostion;
self.monthContainerViewContentView.frame = frame;
}
self.userInteractionEnabled = NO;
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
for (NSInteger index = 0; index < activeMonthViews.count; index++) {
DSLCalendarMonthView *monthView = [activeMonthViews objectAtIndex:index];
for (DSLCalendarDayView *dayView in monthView.dayViews) {
// Use a transition so it fades between states nicely
[UIView transitionWithView:dayView duration:animationDuration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
dayView.inCurrentMonth = (index == 2);
} completion:NULL];
}
}
// Animate the content view to show the target month
CGRect frame = self.monthContainerViewContentView.frame;
frame.origin.y = -restingVerticalPosition;
self.monthContainerViewContentView.frame = frame;
// Resize the container view to show the height of the target month
frame = self.monthContainerView.frame;
frame.size.height = restingHeight;
self.monthContainerView.frame = frame;
// Resize the our frame to show the height of the target month
frame = self.frame;
frame.size.height = CGRectGetMaxY(self.monthContainerView.frame);
self.frame = frame;
// Tell the delegate method that we're about to animate to a new month
if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:#selector(calendarView:willChangeToVisibleMonth:duration:)]) {
[self.delegate calendarView:self willChangeToVisibleMonth:[month copy] duration:animationDuration];
}
} completion:^(BOOL finished) {
self.userInteractionEnabled = YES;
if (finished) {
// Tell the delegate method that we've animated to a new month
if (monthComparisonResult != NSOrderedSame && [self.delegate respondsToSelector:#selector(calendarView:didChangeToVisibleMonth:)]) {
[self.delegate calendarView:self didChangeToVisibleMonth:[month copy]];
}
}
}];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
{
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
self.draggingStartDay = touchedView.day;
self.draggingFixedDay = touchedView.day;
self.draggedOffStartDay = NO;
DSLCalendarRange *newRange = self.selectedRange;
if (self.selectedRange == nil) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
else if (![self.selectedRange.startDay isEqual:touchedView.day] && ![self.selectedRange.endDay isEqual:touchedView.day]) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
else if ([self.selectedRange.startDay isEqual:touchedView.day]) {
self.draggingFixedDay = self.selectedRange.endDay;
}
else {
self.draggingFixedDay = self.selectedRange.startDay;
}
if ([self.delegate respondsToSelector:#selector(calendarView:didDragToDay:selectingRange:)]) {
newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
}
self.selectedRange = newRange;
[self positionCalloutViewForDayView:touchedView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
{
if (self.draggingStartDay == nil) {
return;
}
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
DSLCalendarRange *newRange;
if ([touchedView.day.date compare:self.draggingFixedDay.date] == NSOrderedAscending) {
newRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:self.draggingFixedDay];
}
else {
newRange = [[DSLCalendarRange alloc] initWithStartDay:self.draggingFixedDay endDay:touchedView.day];
}
if ([self.delegate respondsToSelector:#selector(calendarView:didDragToDay:selectingRange:)]) {
newRange = [self.delegate calendarView:self didDragToDay:touchedView.day selectingRange:newRange];
}
self.selectedRange = newRange;
if (!self.draggedOffStartDay) {
if (![self.draggingStartDay isEqual:touchedView.day]) {
self.draggedOffStartDay = YES;
}
}
[self positionCalloutViewForDayView:touchedView];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
{
if (self.draggingStartDay == nil)
{
return;
}
DSLCalendarDayView *touchedView = [self dayViewForTouches:touches];
if (touchedView == nil) {
self.draggingStartDay = nil;
return;
}
if (!self.draggedOffStartDay && [self.draggingStartDay isEqual:touchedView.day]) {
self.selectedRange = [[DSLCalendarRange alloc] initWithStartDay:touchedView.day endDay:touchedView.day];
}
self.draggingStartDay = nil;
// Check if the user has dragged to a day in an adjacent month
if (touchedView.day.year != _visibleMonth.year || touchedView.day.month != _visibleMonth.month)
{
// Ask the delegate if it's OK to animate to the adjacent month
BOOL animateToAdjacentMonth = YES;
if ([self.delegate respondsToSelector:#selector(calendarView:shouldAnimateDragToMonth:)])
{
animateToAdjacentMonth = [self.delegate calendarView:self shouldAnimateDragToMonth:[touchedView.dayAsDate dslCalendarView_monthWithCalendar:_visibleMonth.calendar]];
}
if (animateToAdjacentMonth)
{
if ([touchedView.dayAsDate compare:_visibleMonth.date] == NSOrderedAscending)
{
[self didTapMonthBack:nil];
}
else
{
[self didTapMonthForward:nil];
}
}
}
if ([self.delegate respondsToSelector:#selector(calendarView:didSelectRange:)])
{
[self.delegate calendarView:self didSelectRange:self.selectedRange];
}
}
calendarView.h
#import "DSLCalendarRange.h"
#import "NSDate+DSLCalendarView.h"
#protocol DSLCalendarViewDelegate;
#interface DSLCalendarView : UIView
#property (nonatomic, weak) id<DSLCalendarViewDelegate>delegate;
#property (nonatomic, copy) NSDateComponents *visibleMonth;
#property (nonatomic, strong) DSLCalendarRange *selectedRange;
#property (nonatomic, assign) BOOL showDayCalloutView;
#property (nonatomic, assign) BOOL daysSelected;
+ (Class)monthSelectorViewClass;
+ (Class)monthViewClass;
+ (Class)dayViewClass;
- (void)setVisibleMonth:(NSDateComponents *)visibleMonth animated:(BOOL)animated;
- (void)commonInit;
#end
#protocol DSLCalendarViewDelegate <NSObject>
#optional
- (void)calendarView:(DSLCalendarView*)calendarView didSelectRange:(DSLCalendarRange*)range;
- (void)calendarView:(DSLCalendarView *)calendarView willChangeToVisibleMonth:(NSDateComponents*)month duration:(NSTimeInterval)duration;
- (void)calendarView:(DSLCalendarView *)calendarView didChangeToVisibleMonth:(NSDateComponents*)month;
- (DSLCalendarRange*)calendarView:(DSLCalendarView*)calendarView didDragToDay:(NSDateComponents*)day selectingRange:(DSLCalendarRange*)range;
- (BOOL)calendarView:(DSLCalendarView *)calendarView shouldAnimateDragToMonth:(NSDateComponents*)month;
I need to add additional UIWindow to my App. This UIWindow should always be positioned in the bottom right corner of the screen despite device's orientation, here's the sketch:
I've tried to subclass UIWindow like this to be able to set the size and the margins of window:
#interface MyWindow : UIWindow
#property (nonatomic) CGSize size;
#property (nonatomic) CGFloat margin;
#end
#implementation MyWindow
- (id)initWithSize:(CGSize)size andMargin:(CGFloat)margin {
self.size = size;
self.margin = margin;
return [self initWithFrame:[self calculateFrame]];
}
- (CGRect)calculateFrame {
return CGRectMake([[UIScreen mainScreen] bounds].size.width-self.size.width-self.margin, [[UIScreen mainScreen] bounds].size.height-self.size.height-self.margin, self.size.width, self.size.height);
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self assignObservers];
}
return self;
}
-(void)assignObservers {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(statusBarDidChangeFrame:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)statusBarDidChangeFrame:(NSNotification *)notification {
[self setFrame:[self calculateFrame]];
}
#end
Everything is great on startup! No matter what orientation is on initial start new UIWindow position is correct. But when I rotate device - my window goes crazy, it jumps to unexpected positions and I can't figure out why.
Please help!
Everything works fine if:
dispatch_async(dispatch_get_main_queue(), ^{
[self setFrame:[self calculateFrame]];
});
So full working code looks like this:
#interface MyWindow : UIWindow
#property (nonatomic) CGSize size;
#property (nonatomic) CGFloat margin;
#end
#implementation MyWindow
- (id)initWithSize:(CGSize)size andMargin:(CGFloat)margin {
self.size = size;
self.margin = margin;
return [self initWithFrame:[self calculateFrame]];
}
- (CGRect)calculateFrame {
return CGRectMake([[UIScreen mainScreen] bounds].size.width-self.size.width-self.margin, [[UIScreen mainScreen] bounds].size.height-self.size.height-self.margin, self.size.width, self.size.height);
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self assignObservers];
}
return self;
}
-(void)assignObservers {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(statusBarDidChangeFrame:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
}
- (void)statusBarDidChangeFrame:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(), ^{
[self setFrame:[self calculateFrame]];
});
}
#end
Many thanks to Cocoa-Chat and max.lunin :)
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 a requirement in which the view contains one native UITextField and one UIWebView. The issue is if I switch the focus from UITextView to UIWebView, the keyboard window flicker(hides and then shows).
ie, I got UIKeyboardWillHideNotification and UIKeyboardDidShowNotification
But, this is not happening when I switch the other way. ies, I got only UIKeyboardDidShowNotification
Is there any way to avoid this flickering effect?
Note: I also notices if I have multiple UITextField and UIWebView, this issue is not happening with the same type of views.
I've solved this problem through the code below. Simply set webView.usesGUIFixes = YES; and it should solve your problem. The code below also lets you set a custom input view to use for the UIWebView keyboard:
UIWebView+GUIFixes.h
#import <UIKit/UIKit.h>
#interface UIWebView (GUIFixes)
/**
* #brief The custom input accessory view.
*/
#property (nonatomic, strong, readwrite) UIView* customInputAccessoryView;
/**
* #brief Wether the UIWebView will use the fixes provided by this category or not.
*/
#property (nonatomic, assign, readwrite) BOOL usesGUIFixes;
#end
UIWebView+GUIFixes.m
#import "UIWebView+GUIFixes.h"
#import <objc/runtime.h>
#implementation UIWebView (GUIFixes)
static const char* const kCustomInputAccessoryView = "kCustomInputAccessoryView";
static const char* const fixedClassName = "UIWebBrowserViewMinusAccessoryView";
static Class fixClass = Nil;
- (UIView *)browserView
{
UIScrollView *scrollView = self.scrollView;
UIView *browserView = nil;
for (UIView *subview in scrollView.subviews) {
if ([NSStringFromClass([subview class]) hasPrefix:#"UIWebBrowserView"]) {
browserView = subview;
break;
}
}
return browserView;
}
- (id)methodReturningCustomInputAccessoryView
{
UIView* view = [self performSelector:#selector(originalInputAccessoryView) withObject:nil];
if (view) {
UIView* parentWebView = self.superview;
while (parentWebView && ![parentWebView isKindOfClass:[UIWebView class]])
{
parentWebView = parentWebView.superview;
}
UIView* customInputAccessoryView = [(UIWebView*)parentWebView customInputAccessoryView];
if (customInputAccessoryView) {
view = customInputAccessoryView;
}
}
return view;
}
- (BOOL)delayedBecomeFirstResponder
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[super becomeFirstResponder];
});
return YES;
}
- (void)ensureFixedSubclassExistsOfBrowserViewClass:(Class)browserViewClass
{
if (!fixClass) {
Class newClass = objc_allocateClassPair(browserViewClass, fixedClassName, 0);
IMP oldImp = class_getMethodImplementation(browserViewClass, #selector(inputAccessoryView));
class_addMethod(newClass, #selector(originalInputAccessoryView), oldImp, "##:");
IMP newImp = [self methodForSelector:#selector(methodReturningCustomInputAccessoryView)];
class_addMethod(newClass, #selector(inputAccessoryView), newImp, "##:");
objc_registerClassPair(newClass);
IMP delayedFirstResponderImp = [self methodForSelector:#selector(delayedBecomeFirstResponder)];
Method becomeFirstResponderMethod = class_getInstanceMethod(browserViewClass, #selector(becomeFirstResponder));
method_setImplementation(becomeFirstResponderMethod, delayedFirstResponderImp);
fixClass = newClass;
}
}
- (BOOL)usesGUIFixes
{
UIView *browserView = [self browserView];
return [browserView class] == fixClass;
}
- (void)setUsesGUIFixes:(BOOL)value
{
UIView *browserView = [self browserView];
if (browserView == nil) {
return;
}
[self ensureFixedSubclassExistsOfBrowserViewClass:[browserView class]];
if (value) {
object_setClass(browserView, fixClass);
}
else {
Class normalClass = objc_getClass("UIWebBrowserView");
object_setClass(browserView, normalClass);
}
[browserView reloadInputViews];
}
- (UIView*)customInputAccessoryView
{
return objc_getAssociatedObject(self, kCustomInputAccessoryView);
}
- (void)setCustomInputAccessoryView:(UIView*)view
{
objc_setAssociatedObject(self,
kCustomInputAccessoryView,
view,
OBJC_ASSOCIATION_RETAIN);
}
#end