Reputation: 598
I am encountering an issue with my UITableView. The animations for the delete swipe gesture does not work properly. The thing is, in the new template project "Master Detailed" it works well. But not in the project I am currently working in.
I had an other issue before with the animations that was not working after finishing animate the first time. I fixed it by replacing this in my code.
/// New code
- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key || !obj) {
return;
}
[self gl_setObject:obj forKeyedSubscript:key];
}
/// Old code
- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key) {
return;
}
if (!obj) {
obj = [NSNull null];
}
[self gl_setObject:obj forKeyedSubscript:key];
}
And this is the code of the current TableView that is working in the Xcode base project but not in mine.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TempViewController: UIViewController
@end
NS_ASSUME_NONNULL_END
#import "TempViewController.h"
#import "TempTableViewCell.h"
@interface TempViewController () <UITableViewDelegate, UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property NSMutableArray *objects;
@end
@implementation TempViewController
- (void)viewDidLoad {
[super viewDidLoad];
// TableView
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.rowHeight = 80;
[self.tableView registerNib:[UINib nibWithNibName:@"TempTableViewCell" bundle:nil] forCellReuseIdentifier:@"TempTableViewCell"];
// Add button to add element when deleting too much
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
// Add some data to make the bug work
for (int i = 0; i < 100; i++) {
[self insertNewObject:0];
}
}
- (void)insertNewObject:(id)sender {
if (!self.objects) {
self.objects = [[NSMutableArray alloc] init];
}
[self.objects insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.objects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TempTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TempTableViewCell" forIndexPath:indexPath];
NSDate *object = self.objects[indexPath.row];
cell.tempLabel.text = [object description];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.objects removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
@end
So, I am wondering guys if you have any clue about where to look at? Or already experienced this issue before?
Thank you for your future help!! Happy building!
Upvotes: 0
Views: 330
Reputation: 598
Well,
I went deeper in the direction on our swizzling methods. I removed this code completely. We were using it to avoid inserting nil value in dictionary. Without it, the bug disappeared. I am not sure why, but if I found the answer someday, I will update this post. Hope it can save some time for some people!
#import "NSDictionary+NilSafe.h"
#import <objc/runtime.h>
@implementation NSObject (Swizzling)
+ (BOOL)gl_swizzleMethod:(SEL)origSel withMethod:(SEL)altSel {
Method origMethod = class_getInstanceMethod(self, origSel);
Method altMethod = class_getInstanceMethod(self, altSel);
if (!origMethod || !altMethod) {
return NO;
}
class_addMethod(self,
origSel,
class_getMethodImplementation(self, origSel),
method_getTypeEncoding(origMethod));
class_addMethod(self,
altSel,
class_getMethodImplementation(self, altSel),
method_getTypeEncoding(altMethod));
method_exchangeImplementations(class_getInstanceMethod(self, origSel),
class_getInstanceMethod(self, altSel));
return YES;
}
+ (BOOL)gl_swizzleClassMethod:(SEL)origSel withMethod:(SEL)altSel {
return [object_getClass((id)self) gl_swizzleMethod:origSel withMethod:altSel];
}
@end
@implementation NSDictionary (NilSafe)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self gl_swizzleMethod:@selector(initWithObjects:forKeys:count:) withMethod:@selector(gl_initWithObjects:forKeys:count:)];
[self gl_swizzleClassMethod:@selector(dictionaryWithObjects:forKeys:count:) withMethod:@selector(gl_dictionaryWithObjects:forKeys:count:)];
});
}
+ (instancetype)gl_dictionaryWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt {
id safeObjects[cnt];
id safeKeys[cnt];
NSUInteger j = 0;
for (NSUInteger i = 0; i < cnt; i++) {
id key = keys[i];
id obj = objects[i];
if (!key) {
continue;
}
if (!obj) {
obj = [NSNull null];
}
safeKeys[j] = key;
safeObjects[j] = obj;
j++;
}
return [self gl_dictionaryWithObjects:safeObjects forKeys:safeKeys count:j];
}
- (instancetype)gl_initWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt {
id safeObjects[cnt];
id safeKeys[cnt];
NSUInteger j = 0;
for (NSUInteger i = 0; i < cnt; i++) {
id key = keys[i];
id obj = objects[i];
if (!key) {
continue;
}
if (!obj) {
obj = [NSNull null];
}
safeKeys[j] = key;
safeObjects[j] = obj;
j++;
}
return [self gl_initWithObjects:safeObjects forKeys:safeKeys count:j];
}
@end
@implementation NSMutableDictionary (NilSafe)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = NSClassFromString(@"__NSDictionaryM");
[class gl_swizzleMethod:@selector(setObject:forKey:) withMethod:@selector(gl_setObject:forKey:)];
[class gl_swizzleMethod:@selector(setObject:forKeyedSubscript:) withMethod:@selector(gl_setObject:forKeyedSubscript:)];
});
}
- (void)gl_setObject:(id)anObject forKey:(id<NSCopying>)aKey {
if (!aKey) {
return;
}
if (!anObject) {
anObject = [NSNull null];
}
[self gl_setObject:anObject forKey:aKey];
}
- (void)gl_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (!key) {
return;
}
if (!obj) {
obj = [NSNull null];
}
[self gl_setObject:obj forKeyedSubscript:key];
}
@end
@implementation NSNull (NilSafe)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self gl_swizzleMethod:@selector(methodSignatureForSelector:) withMethod:@selector(gl_methodSignatureForSelector:)];
[self gl_swizzleMethod:@selector(forwardInvocation:) withMethod:@selector(gl_forwardInvocation:)];
});
}
- (NSMethodSignature *)gl_methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig = [self gl_methodSignatureForSelector:aSelector];
if (sig) {
return sig;
}
return [NSMethodSignature signatureWithObjCTypes:@encode(void)];
}
- (void)gl_forwardInvocation:(NSInvocation *)anInvocation {
NSUInteger returnLength = [[anInvocation methodSignature] methodReturnLength];
if (!returnLength) {
// nothing to do
return;
}
// set return value to all zero bits
char buffer[returnLength];
memset(buffer, 0, returnLength);
[anInvocation setReturnValue:buffer];
}
@end
EDIT 1: Well, at the end, the problem was this nil check of obj
if (!key || !obj) {
return;
}
/// Replaced by
if (!key) {
return;
}
I kept the rest of the code, it was producing a crash in case of inserting trying to insert nil value in a NSDictionary.
Upvotes: 0