I've been brought in on this project where the previous developers made custom table cells and headers by using xib files and then registering the nibs like so:
[self.accountTable registerNib:[UINib nibWithNibName:kNonATITableViewCellLandscapeNib bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kNonATITableViewCellLandscapeIdentifier];
[self.accountTable registerNib:[UINib nibWithNibName:kNonATITableHeaderLandscapeNib bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kNonATITableHeaderLandscapeId];
The header files have buttons in them and uiimageviews. The buttons are for sorting, the uiimageviews for an arrow icon to show you the direction of the sort (asc, desc). All the buttons and imageviews are IBOutlets. All the buttons are linked to an IBAction:
- (IBAction)sortButtonTouched:(id)sender;
The file also has two other properties:
#property (nonatomic, assign) SortType currentSortingOption;
#property (nonatomic, strong) UIButton* btnLastTouched;
Here is sortButtonTouched:
- (IBAction)sortButtonTouched: (UIButton*) buttonTouched {
if (!self.btnLastTouched) {
self.btnLastTouched = buttonTouched;
}
NSString* strFieldToSort;
UIImageView* ivSortImage;
NSArray* arrSortIcons = [[NSArray alloc] initWithObjects:self.ivAccountSort,self.ivNameSort, self.ivAddressSort, self.ivCitySort, self.ivZipSort, self.ivLastCallSort, self.ivMileageSort, nil];
//get the image for the button selected
if (buttonTouched.tag == 0) {
strFieldToSort = #"customerNumber";
ivSortImage = self.ivAccountSort;
} else if (buttonTouched.tag == 1) {
strFieldToSort = #"customerName";
ivSortImage = self.ivNameSort;
} else if (buttonTouched.tag == 2) {
strFieldToSort = #"address";
ivSortImage = self.ivAddressSort;
} else if (buttonTouched.tag == 3) {
strFieldToSort = #"city";
ivSortImage = self.ivCitySort;
} else if (buttonTouched.tag == 4) {
strFieldToSort = #"zip";
ivSortImage = self.ivZipSort;
} else if (buttonTouched.tag == 5) {
strFieldToSort = #"lastCallDate";
ivSortImage = self.ivLastCallSort;
} else if (buttonTouched.tag == 6) {
strFieldToSort = #"mileage";
ivSortImage = self.ivMileageSort;
}
//set the sort option and add icon
if (!self.currentSortingOption) {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
} else {
if (![self.btnLastTouched isEqual:buttonTouched]) {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
} else {
if (self.currentSortingOption == SORT_ASC) {
self.currentSortingOption = SORT_DESC;
[ivSortImage setImage:[UIImage imageNamed:Descending_Icon]];
} else {
self.currentSortingOption = SORT_ASC;
[ivSortImage setImage:[UIImage imageNamed:Ascending_Icon]];
}
}
}
//show and hide
for(int i=0; i<arrSortIcons.count; i++) {
UIImageView* ivThisImage = [arrSortIcons objectAtIndex:i];
if (buttonTouched.tag == i) {
[UIView animateWithDuration:.25 animations:^(void) {
ivThisImage.alpha = 1.0;
}];
} else {
[UIView animateWithDuration:.25 animations:^(void) {
ivThisImage.alpha = 0.0;
}];
}
}
//call back to routing view controller and sort results based on sort order and field selected
NSDictionary* dictUserData = [[NSDictionary alloc] initWithObjectsAndKeys:
#"Sort Non-ATI", #"Action",
strFieldToSort, #"Field To Sort",
[NSNumber numberWithLong:self.currentSortingOption], #"Sortng Option",
nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"rvc" object:self userInfo:dictUserData];
self.btnLastTouched = buttonTouched;
}
And the notification fires this method:
- (void) sortNonATIResults : (NSDictionary*) dictSortParams {
if (self.arrNonATIResults.count > 0) {
NSString* sortKey = [dictSortParams objectForKey:#"Field To Sort"];
//change the field to sort to match the customerInfo object properties...
NSNumber* numSortType = [dictSortParams objectForKey:#"Sortng Option"];
BOOL isAsc = YES;
if ([numSortType intValue] == 2) {
isAsc = NO;
}
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:isAsc];
NSArray* arrSortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray* arrSortedNonATIResults = (NSArray*)[self.arrNonATIResults sortedArrayUsingDescriptors:arrSortDescriptors];
self.arrNonATIResults = [arrSortedNonATIResults mutableCopy];
self.arrDatasource = self.arrNonATIResults;
[self.accountTable reloadData];
}
}
There are two problems right now. The icons are not showing up if the notification is sent. Comment out the notification and they function as expected. The other problem is that the property currentSortingOption doesn't retain it's value. I think both issues are related but I am not 100% sure. When the tableview is reloaded, does the header get instantiated again? This would make sense to me since then the uiimageviews would be reset with no image and the property would lose it's value and reset to 0 (it is the value of a typedef).
So, I am correct, how can I resolve this and if not, what could be causing the problems?
Thanks
OK, sorry for posting and then solving my problem right away, I guess sometimes you just need to write out the problem to find the solution. All I needed to do was not reload the table but just reload the rows. Here's the updated method:
(void) sortNonATIResults : (NSDictionary*) dictSortParams {
if (self.arrNonATIResults.count > 0) {
NSString* sortKey = [dictSortParams objectForKey:#"Field To Sort"];
//change the field to sort to match the customerInfo object properties...
NSNumber* numSortType = [dictSortParams objectForKey:#"Sortng Option"];
BOOL isAsc = YES;
if ([numSortType intValue] == 2) {
isAsc = NO;
}
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:isAsc];
NSArray* arrSortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray* arrSortedNonATIResults = (NSArray*)[self.arrNonATIResults sortedArrayUsingDescriptors:arrSortDescriptors];
self.arrNonATIResults = [arrSortedNonATIResults mutableCopy];
self.arrDatasource = self.arrNonATIResults;
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableArray *indexPathArray = [[NSMutableArray alloc] init];
for (NSInteger section = 0; section < [self.accountTable numberOfSections]; ++section)
{
for (NSInteger row = 0; row < [self.accountTable numberOfRowsInSection:section]; ++row)
{
[indexPathArray addObject:[NSIndexPath indexPathForRow:row inSection:section]];
}
}
[self.accountTable reloadRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationNone];
[self.accountTable scrollsToTop];
});
}
}
Related
This below code is fired when I press delete button after selecting messages I want to delete in chat room.
- (void)deleteButtonPressed:(id)sender {
if (arrayToDelete.count) {
for (NSString *str in arrayToDelete) {
NSLog(#"msgID --> %#",str);
[self.chatModel.dataSource removeObject:str]; //??? Remove data from the screen
[[FMDBManager sharedInstance] deleteMessageByMessageId:str]; //??? Delete data from database
}
[arrayToDelete removeAllObjects];
[self.chatTableView reloadData];
}
}
This line successfully removes selected messages from the chat room.
[self.chatModel.dataSource removeObject:str]; //??? Remove data from the screen
When I go out the chat room and re-enter, those messages still exist, so I have this line below.
[[FMDBManager sharedInstance] deleteMessageByMessageId:str]; //??? Delete data from database
I think the above line should delete those selected messages from the database but when I re-enter the chat room I still see those messages. Here below are related code to that.
- (void)deleteMessageByMessageId:(NSString *)messageId {
FMDatabase *db = [self getterDataBase];
[db open];
NSString *sqlString = [NSString stringWithFormat:#"DELETE FROM message WHERE messageId = '%#'",messageId];
BOOL status = [db executeUpdate:sqlString];
NSLog(#"Delete MessageById:%# Status:%d",messageId,status);
[db close];
}
I've found that when chat room calls viewDidLoad it will eventually call the method callBackGetChannelLogNew where server will sync-up data with chat room tableview and local database.
- (void)callBackGetChannelLogNew:(NSDictionary *)resultDataDic status:(enumAPI_STATUS)eAPI_STATUS {
if (isFirstTimeUpdate) {
}
if (eAPI_STATUS == API_STATUS_SUCCEE) {
NSString *readString=[NSString stringWithFormat:#"%#",resultDataDic[#"read_arr"]];
if ([readString isEqualToString:#""]) {
// NSLog(#"read_arr is empty");
}
else {
NSArray *read_arr=resultDataDic[#"read_arr"];
// Copy read_arr
self.readArray=[read_arr mutableCopy];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
[self dealWithReadArray:read_arr];
});
}
NSArray *data = [resultDataDic objectForKey:#"msg"];
if (data.count > 0) {
apiHaveData = YES;
} else {
apiHaveData = NO;
self.loadIngView.hidden = YES;
isLoadingData = NO;
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
// Reverse order of data
NSArray* reversedArray = [[data reverseObjectEnumerator] allObjects];
NSMutableArray *messageFromOtherArray = [NSMutableArray new];
NSMutableArray *messageAllArray = [NSMutableArray new];
for (int i = 0; i < reversedArray.count; i++) {
NSDictionary *_dic = reversedArray[i];
NSString *fromId = [_dic objectForKey:#"fid"];
NSString *message = [NSString stringWithFormat:#"%#",[_dic objectForKey:#"say"]];
if ([ObjectManager getChatMessageKindWithString:message] == MessageTypeText) {
message = [ObjectManager decryptWithString:message];
}
NSString *messageId = [_dic objectForKey:#"mid"];
NSString *toId = [_dic objectForKey:#"tid"];
NSDateFormatter *_formatter = [[NSDateFormatter alloc] init];
_formatter.dateFormat = #"yyyy-MM-dd HH:mm:ss.SSS";
NSDate *date_t = [NSDate dateWithTimeIntervalSince1970:[[_dic objectForKey:#"t"] doubleValue]/1000.0]; //換算成日期
NSString *stringDate = [_formatter stringFromDate:date_t];
NSString *sendDate = stringDate;
NSString *lid = _dic[#"lid"];
NSMutableDictionary *myDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
fromId,#"fromId",
message,#"message",
messageId,#"messageId",
sendDate,#"sendDate",
toId,#"toId",
lid,#"lid",
nil];
NSString *isRead;
if (_chatRoomType == ChatRoomTypePrivate) {
if ([_dic[#"r"] intValue]) {
isRead = #"1";
myDic[#"isRead"] = isRead;
lastReadMessageId = [NSString stringWithFormat:#"%#",messageId];
}
}
if (i == 0) {
if (lidForAPI != [_dic[#"lid"] intValue]) {
lidForAPI = [_dic[#"lid"] intValue];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
apiHaveData = NO;
self.loadIngView.hidden = YES;
isLoadingData = NO;
});
return ;
}
}
if (![myDic[#"fromId"] isEqualToString:[User sharedUser].account]) {
[messageFromOtherArray addObject:myDic];
}
if (_chatRoomType == ChatRoomTypeGroup) {
[myDic setObject:#"1" forKey:#"isGroupMessage"];
}
[myDic setObject:#"1" forKey:#"did_I_Read"];
[messageAllArray addObject:myDic];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self setupViewWithMessageArray:messageAllArray]; //???? Here server sync-up data with tableview
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
if (_chatRoomType == ChatRoomTypePrivate) {
if (messageFromOtherArray.count > 0 && isUplaodLastRead == NO) {
isUplaodLastRead = YES;
NSDictionary *lastReadMsgDic = messageFromOtherArray.lastObject;
[self callMsgReadAPI:lastReadMsgDic];
}
} else {
if (messageAllArray.count > 0 && isUplaodLastRead == NO) {
isUplaodLastRead = YES;
NSDictionary *lastReadMsgDic = messageAllArray.lastObject;
[self callMsgReadAPI:lastReadMsgDic];
}
}
self.chatModel.channelTopic = _topic;
NSArray *read_arr=resultDataDic[#"read_arr"];
[self dealMySendMessageReadedWithReadArray:read_arr AndMessageArray:messageAllArray];
[self saveMessageWithArray:messageAllArray]; //???? Here server sync-up data with local db
});
});
}
}
This lines will sync-up data from server to tableview
dispatch_async(dispatch_get_main_queue(), ^{
[self setupViewWithMessageArray:messageAllArray]; //???? Here server sync-up data with tableview
});
Here below is the method setupViewWithMessageArray
- (void)setupViewWithMessageArray:(NSArray *)messageAllArray {
if (!isFirstTimeUpdate) {
isFirstTimeUpdate = YES;
self.chatModel.dataSource = nil;
[self.chatTableView reloadData];
self.chatModel.dataSource = [[NSMutableArray alloc] init];
[self addMessageWithArray:messageAllArray];
[self.chatTableView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.chatModel.dataSource.count-1 inSection:0];
[self.chatTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
} else {
[self addMessageWithArray:messageAllArray];
[self reloadTableViewWithoutMove];
}
self.loadIngView.hidden = YES;
isLoadingData = NO;
if (_chatRoomType == ChatRoomTypePrivate) {
if (lastReadMessageId) {
[self.chatModel setPrivateChatListAllReadFormMessageId:lastReadMessageId];
}
}
}
This line will sync-up data from server to local db
[self saveMessageWithArray:messageAllArray]; //???? Here server sync-up data with local db
Here below is the method saveMessageWithArray
- (void)saveMessageWithArray:(NSArray *)messageArray {
for (NSDictionary *myDic in messageArray) {
if (![[FMDBManager sharedInstance] didMessageExistWithMessageID:[myDic objectForKey:#"messageId"]]) {
[[FMDBManager sharedInstance] SaveMessage:myDic];
}
else {
NSString *mid=[NSString stringWithFormat:#"%#",myDic[#"messageId"]];
NSString *isRead = myDic[#"isReaed"];
if (isRead) {
[[FMDBManager sharedInstance] UpdateisReadWithMessageID:mid];
}
}
}
}
So I think now my question is how I can update messageAllArray with arrayToDelete before server sync-up?
I implemented “swipe to delete cell in TableView” and it works fine in iOS 7 but that same code is not working for iOS 8. Is there any changes done for iOS 8 in this?
After swiping the cell I see nothing in my “Groups” section in iOS 8 while in iOS 7 I see a delete button.
Here is the code I am using:
- (void)swipeableTableViewCell:(SWTableViewCell *)cell scrollingToState: (SWCellState)state
{
switch (state) {
case 0:
NSLog(#"utility buttons closed");
[customDropDown hideDropDown:dropDownFrame];
[customDropDown removeFromSuperview];
customDropDown = nil;
break;
case 1:
NSLog(#"left utility buttons open");
break;
case 2:
NSLog(#"right utility buttons open");
break;
default:
break;
}
}
- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index
{
switch (index)
{
case 0:
{
if (cell.rightUtilityButtons.count == 1)
{
NSArray * arr = [[NSArray alloc] init];
arr = [NSArray arrayWithArray:[self.groupsInfoDict allKeys]];
if(customDropDown == nil)
{
indexPathForSelectedcontact = [self.groupListTableView indexPathForCell:cell];
CGRect frame = [self.groupListTableView rectForRowAtIndexPath:indexPathForSelectedcontact];
CGRect animatedViewFrame = [self.view convertRect:frame fromView:self.groupListTableView];
CGFloat height = self.view.frame.size.height-(animatedViewFrame.origin.y+44);
NSString* direction;
if (height<88)
{
direction = #"up";
CGFloat noOfRowHeight = [arr count]*40;
if (noOfRowHeight <animatedViewFrame.origin.y)
{
height = noOfRowHeight;
}
else
{
height = animatedViewFrame.origin.y;
}
}
else
{
direction = #"down";
CGFloat noOfRowHeight = [arr count]*40;
if (noOfRowHeight <height)
{
height = noOfRowHeight;
}
}
dropDownFrame = CGRectMake(animatedViewFrame.origin.x, animatedViewFrame.origin.y, 160,height);
customDropDown = [[CustomDropDown alloc]showDropDown:dropDownFrame arrayOfData:arr ArrayOfimages:nil inDirection:direction onView:self.view];
customDropDown.delegate = self;
}
else
{
[customDropDown hideDropDown:dropDownFrame];
[customDropDown removeFromSuperview];
dropDownFrame = CGRectZero;
customDropDown = nil;
}
}
else
{
NSIndexPath* indexpath = [self.groupListTableView indexPathForCell:cell];
NSArray* arrayOfKeys = (NSArray *)self.groupsInfoDict.allKeys;
NSArray *sortedValues = [arrayOfKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
groupTitleSelectedForRename = [sortedValues objectAtIndex:indexpath.row];
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Events" bundle:nil];
AddANewGroupViewController *addANewGroupViewController = [storyboard instantiateViewControllerWithIdentifier:#"AddANewGroupViewController"];
addANewGroupViewController.screenType = kScreenEditGroup;
addANewGroupViewController.useTableIndex = YES;
addANewGroupViewController.allowSearchControl = YES;
addANewGroupViewController.groupTitleSelectedForRename = groupTitleSelectedForRename;
[self.navigationController pushViewController:addANewGroupViewController animated:YES];
//[self getGroupNameAlert:groupTitleSelectedForRename ifAlreadyExists:NO];
}
break;
}
case 1:
{
NSIndexPath* indexPath = [self.groupListTableView indexPathForCell:cell];
// Change the cell appearance
NSArray* arrayOfKeys = (NSArray *)self.groupsInfoDict.allKeys;
NSArray *sortedValues = [arrayOfKeys sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
selectedGroupKey = [sortedValues objectAtIndex:indexPath.row];
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"" message:#"Are you sure you want to delete this group?" delegate:self cancelButtonTitle:#"NO" otherButtonTitles:#"YES", nil];
[alert setTag:990];
[alert show];
break;
}
default:
break;
}
}
- (BOOL)swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:(SWTableViewCell *)cell
{
// allow just one cell's utility button to be open at once
return YES;
}
- (BOOL)swipeableTableViewCell:(SWTableViewCell *)cell canSwipeToState:(SWCellState)state{
switch (state) {
case 1:
// set to NO to disable all left utility buttons appearing
return YES;
break;
case 2:
// set to NO to disable all right utility buttons appearing
return YES;
break;
default:
break;
}
return YES;
}
-(void)Aray
{
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
if(Counter < NewColor)
{
[ColorArray addObject:[NSNumber numberWithInteger:ColorTemp]];
Counter += 1;
}
}
-(IBAction)Go:(id)sender
{
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
Color = [[ColorArray objectAtIndex:Index] intValue];
if(Color == 2)
{
ColorLabel.text = #"The Color is Black";
Screen.image = [UIImage imageNamed:#"BlackTile.png"];
}
else
{
Screen.image = [UIImage imageNamed:#"Tunnel.png"];
ColorLabel.text = #"The Color is Green";
}
Index += 1;
}
-(IBAction)Black:(id)sender
{
ColorTemp = 2;
NewColor += 1;
[self Array];
}
-(IBAction)Green:(id)sender
{
ColorTemp = 1;
NewColor += 1;
[self Array];
}
The issue is that the ColorArray needs to be an instance variable (or #property) of the class so that it persists outside of the method calls.
This code will always crash, regardless of the value of Index:
NSMutableArray *ColorArray = [[NSMutableArray alloc] init];
Color = [[ColorArray objectAtIndex:Index] intValue];
Color appears to already be an instance variable (or #property), so this concept should not be alien to you.
Side note: variables conventionally start with lower case and use camal-case naming.
I have 20 images in my main bundle and i want to display it in my scroll view horizontally.
I am doing it successfully but now the problem is images are visible in scrollview after all imageview are set in scroll view and it takes time.
For resolve that I create one nsoperation.
-(BOOL)sync:(NSError**)error
{
float imageLeftPosition = 0;
for (int imageCount = 1; imageCount<=syncObject.syncNumberOfImages; imageCount++) {
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(imageLeftPosition, 0, syncObject.syncImageWidth, syncObject.syncImageHeight)];
[imgView setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%d_%d.jpg",syncObject.syncChapterNumber,imageCount]]];
imgView.tag = imageCount;
// NSDictionary *dictionary = [NSDictionary new];
//
// [dictionary setValue:imgView forKey:CHAPTER_IMAGE];
imageLeftPosition = imageLeftPosition+syncObject.syncImageWidth;
//[dictionary setValue:[NSString stringWithFormat:#"%f",imageLeftPosition] forKey:CHAPTER_SCROLL_LEFT_POSITION];
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:imgView,[NSString stringWithFormat:#"%f",imageLeftPosition], nil] forKeys:[NSArray arrayWithObjects:CHAPTER_IMAGE,CHAPTER_SCROLL_LEFT_POSITION, nil]];
[self sendNotification:dictionary];
imgView = nil;
dictionary = nil;
}
return YES;
}
what i am doing here is add create one image view and post it to main view using notification
-(void)addImageView:(NSNotification*)notification
{
NSInteger thread = [NSThread isMainThread]?CVC_MainThread:CVC_BackgroundThread;
switch (thread) {
case CVC_MainThread:
{
NSDictionary *dictionary = notification.userInfo;
if(dictionary != nil)
{
NSLog(#"image view : %#",(UIImageView*)[dictionary valueForKey:CHAPTER_IMAGE]);
NSLog(#"leftposition: %f",[[dictionary valueForKey:CHAPTER_SCROLL_LEFT_POSITION] floatValue]);
[_scrChapters addSubview:((UIImageView*)[dictionary valueForKey:CHAPTER_IMAGE])];
_scrChapters.contentSize = CGSizeMake([[dictionary valueForKey:CHAPTER_SCROLL_LEFT_POSITION] floatValue], 0);
}
dictionary = nil;
}
break;
case CVC_BackgroundThread:
{
dispatch_async(dispatch_get_main_queue(), ^(void) {
NSDictionary *dictionary = notification.userInfo;
if(dictionary != nil)
{
NSLog(#"image view : %#",(UIImageView*)[dictionary valueForKey:CHAPTER_IMAGE]);
NSLog(#"leftposition: %f",[[dictionary valueForKey:CHAPTER_SCROLL_LEFT_POSITION] floatValue]);
[_scrChapters addSubview:((UIImageView*)[dictionary valueForKey:CHAPTER_IMAGE])];
_scrChapters.contentSize = CGSizeMake([[dictionary valueForKey:CHAPTER_SCROLL_LEFT_POSITION] floatValue], 0);
}
dictionary = nil;
});
}
break;
default:
break;
}
}
this is the notification handler method where i set the content size and image view in scroll view for each loop on main thread still scroll view are visible after all the images are load.
What I am doing wrong?
Try to use pagedflowview. This will load only the visible items into the Scrollview and is easy to customize. You can also search for iCarousel.
https://www.cocoacontrols.com/controls/pagedflowview
Try this
-(void)showimagesasSlide:(int) totalimagesCount
{
int xAxisofimageView=0;
for (int initialimagesCount=0;initialimagesCount<totalimagesCount; initialimagesCount++) {
UIImageView *detailedImageview=[[UIImageView alloc] init];
detailedImageview.userInteractionEnabled=YES;
UIImage *dynamicImage=[ScaleImageSize squareImageWithImage:[images objectAtIndex:initialimagesCount] scaledToSize:CGSizeMake(300, 500) ];
if (IS_IPHONE_5) {
detailedImageview.frame=CGRectMake(xAxisofimageView, 0, 320, 568);
}
else
{
detailedImageview.frame=CGRectMake(xAxisofimageView, 0, 320, 460);
}
// detailedImageview.contentMode=UIViewContentModeCenter;
detailedImageview.backgroundColor=[UIColor clearColor];
detailedImageview.image=dynamicImage;
detailedImageview.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin ;
detailedImageview.tag=initialimagesCount+12;
[mainScrollview addSubview:detailedImageview];
xAxisofimageView=xAxisofimageView+320;
}
mainScrollview.contentSize=CGSizeMake(320*images.count, 0);
}
I have a screen with two text fields (category, subcategory), each hooked up to a custom UIPickerView. The options present in the subcategory view depend on the category selected as the value of the first field.
If the user has not selected a category, selecting the subcategory field brings up the standard keyboard (this behavior is fine).
If the user selects a category and then interacts with the subcategory field everything works fine.
The problem happens when the user puts in a category, brings up the subcategory picker, and then goes back and clears the category field. At this point, if the user selects the subcategory field, the picker will appear without any data and interacting with it will cause the app to crash.
The error text:
*** Assertion failure in -[UITableViewRowData rectForRow:inSection:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableViewRowData.m:1630
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for rect at invalid index path (<NSIndexPath 0x719eb60> 2 indexes [0, 0])'
*** First throw call stack:
(0x1cc3012 0x1100e7e 0x1cc2e78 0xb96665 0x22df20 0xf12de 0x481086 0x480f7a 0xa440d 0xa69eb 0x30f85a 0x30e99b 0x3100df 0x312d2d 0x312cac 0x30aa28 0x77972 0x77e53 0x55d4a 0x47698 0x1c1edf9 0x1c1ead0 0x1c38bf5 0x1c38962 0x1c69bb6 0x1c68f44 0x1c68e1b 0x1c1d7e3 0x1c1d668 0x44ffc 0x2acd 0x29f5)
libc++abi.dylib: terminate called throwing an exception
Here is my code:
- (IBAction)showYourPicker:(id)sender {
isCategoryPicker = true;
// create a UIPicker view as a custom keyboard view
UIPickerView* pickerView = [[UIPickerView alloc] init];
[pickerView sizeToFit];
pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
pickerView.delegate = self;
pickerView.dataSource = self;
pickerView.showsSelectionIndicator = YES;
self.catPickView = pickerView; //UIPickerView
categoryField.inputView = pickerView;
// create a done view + done button, attach to it a doneClicked action, and place it in a toolbar as an accessory input view...
// Prepare done button
UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
keyboardDoneButtonView.barStyle = UIBarStyleBlack;
keyboardDoneButtonView.translucent = YES;
keyboardDoneButtonView.tintColor = nil;
[keyboardDoneButtonView sizeToFit];
UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered target:self
action:#selector(pickerDoneClicked:)];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];
// Plug the keyboardDoneButtonView into the text field...
categoryField.inputAccessoryView = keyboardDoneButtonView;
}
- (IBAction)showYourSubPicker:(id)sender {
isCategoryPicker = false;
WCSharedCache *sharedManager = [WCSharedCache sharedManager];
BOOL iLLAllowIt = false;
for(int i = 0; i < [sharedManager.categories count]; i++) {
if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
iLLAllowIt = true;
}
}
if(!iLLAllowIt) {
return;
}
// create a UIPicker view as a custom keyboard view
UIPickerView* pickerView = [[UIPickerView alloc] init];
[pickerView sizeToFit];
pickerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
pickerView.delegate = self;
pickerView.dataSource = self;
pickerView.showsSelectionIndicator = YES;
self.subCatPickView = pickerView; //UIPickerView
subcategoryField.inputView = pickerView;
// create a done view + done button, attach to it a doneClicked action, and place it in a toolbar as an accessory input view...
// Prepare done button
UIToolbar* keyboardDoneButtonView = [[UIToolbar alloc] init];
keyboardDoneButtonView.barStyle = UIBarStyleBlack;
keyboardDoneButtonView.translucent = YES;
keyboardDoneButtonView.tintColor = nil;
[keyboardDoneButtonView sizeToFit];
UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered target:self
action:#selector(pickerDoneClicked:)];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];
// Plug the keyboardDoneButtonView into the text field...
subcategoryField.inputAccessoryView = keyboardDoneButtonView;
}
- (void) pickerDoneClicked: (id) picker {
if(isCategoryPicker) {
[categoryField resignFirstResponder];
} else {
[subcategoryField resignFirstResponder];
}
}
- (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
WCSharedCache *sharedManager = [WCSharedCache sharedManager];
if(isCategoryPicker) {
[categoryField setText:[[sharedManager categories] objectAtIndex:row]];
} else {
#try {
int idx = 0;
for(int i = 0; i < [sharedManager.categories count]; i++) {
if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
idx = i;
break;
}
}
[subcategoryField setText:[[[sharedManager subcategories]objectAtIndex:idx] objectAtIndex:row]];
} #catch (NSException *e) {
NSLog(#"Exception: %#",e);
[subcategoryField setText:#"" ];
}
}
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
WCSharedCache *sharedManager = [WCSharedCache sharedManager];
if(isCategoryPicker) {
return [[sharedManager categories]count];
} else {
for(int i = 0; i < [sharedManager.categories count]; i++) {
if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
return [[[sharedManager subcategories] objectAtIndex:i] count];
}
}
}
return 0;
}
- (NSString *)pickerView: (UIPickerView *)pickerView titleForRow: (NSInteger)row forComponent:(NSInteger)component
{
WCSharedCache *sharedManager = [WCSharedCache sharedManager];
if(isCategoryPicker) {
return [[sharedManager categories] objectAtIndex:row];
} else {
for(int i = 0; i < [sharedManager.categories count]; i++) {
if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
return [[[sharedManager subcategories] objectAtIndex:i] objectAtIndex:row];
}
}
}
return #"";
}
Is there a method I can implement, or a try-catch, or revival of original keyboard, or somewhere I can prevent the user from hitting this field if there is no data any of those would work. The data for categories is in an NSArray. The data for subcategories is in a two-dimensional NSArray indexed off of the index of the associated category.
If fixed this problem by changing the default value in:
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
WCSharedCache *sharedManager = [WCSharedCache sharedManager];
if(isCategoryPicker) {
return [[sharedManager categories]count];
} else {
for(int i = 0; i < [sharedManager.categories count]; i++) {
if([[[sharedManager categories] objectAtIndex:i] isEqualToString:[categoryField text]]) {
return [[[sharedManager subcategories] objectAtIndex:i] count];
}
}
}
return 1;
}
from zero to one. Apparently, UIPickerView (at least in the implementation above) cannot handle scrolling without throwing an exception if the number of rows in the component is 0. This is curious, because according the Apple's documentation of the UIPickerView class, the default value for this method is 0.