奇怪的UICollectionView行为

时间:2014-06-30 16:42:35

标签: ios objective-c nsmutablearray uicollectionview uicollectionviewcell

我正在为iPad开发一个应用程序,用户可以在其中添加和删除UICollectionView中的项目。

这是我的整个ViewController代码:

#import "ProjectsViewController.h"
#import "DetailViewController.h"
#import "MasterViewController.h"
#import "MyManager.h"

@interface ProjectsViewController ()
{
// Declare variables
NSArray *projects;
NSString *selectedProject;
NSMutableArray *_objects;
BOOL deleteAlert;
int editedProjects;
}
@property (weak, nonatomic) IBOutlet UICollectionView *myCollectionView;

@end

@implementation ProjectsViewController
{
// Declare variables
NSString *deletedProject;
}
@synthesize prepareButton, aboutButton, createButton, editButton, editMode;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
    // Custom initialization
}
return self;
}

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[_projectsCollectionView registerNib:[UINib nibWithNibName:@"Cell" bundle:nil] forCellWithReuseIdentifier:@"Cell"];

// Configure the style of the three big buttons
prepareButton.layer.cornerRadius = 7;
prepareButton.layer.borderWidth = 1;
prepareButton.layer.borderColor = [UIColor blueColor].CGColor;

aboutButton.layer.cornerRadius = 7;
aboutButton.layer.borderWidth = 1;
aboutButton.layer.borderColor = [UIColor blueColor].CGColor;

createButton.layer.cornerRadius = 7;
createButton.layer.borderWidth = 1;
createButton.layer.borderColor = [UIColor blueColor].CGColor;

editButton.layer.cornerRadius = 7;
editButton.layer.borderWidth = 1;
editButton.layer.borderColor = [UIColor blueColor].CGColor;
// (note - may prefer to use the tintcolor of the control)

// Set up projectsCollectionView
_projectsCollectionView.delegate = self;
_projectsCollectionView.dataSource = self;

#define saveNewProjects @"saveNewProjects"
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resaveProjects:) name:saveNewProjects object:_projectCellClass];

#define deleteProject @"deleteProject"
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deleteProjects:) name:deleteProject object:_projectCellClass];
}

- (void)resaveProjects:(NSNotification *)notification {
// Edit the edited projects and save all again
editedProjects = editedProjects - 1;

NSString *previousName;
NSString *newName;

// Create a MyManger instance
MyManager *sharedManager = [MyManager sharedManager];

// Set the new and old data from MyManager
previousName = sharedManager.oldProjectName;
newName = sharedManager.changedProjectName;

// Find that data in the _objects NSMutableArray
int projectIndex = [_objects indexOfObject:previousName];
[_objects removeObject:previousName];
[_objects insertObject:newName atIndex:projectIndex];

// Resave _objects
[[NSUserDefaults standardUserDefaults] setObject:_objects forKey:@"myProjects"];

// Change the associated subjects to the new key
// Get the subjects
NSMutableArray *projectSubjects = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:previousName]];

// Remove the subjects for the previous key
[[NSUserDefaults standardUserDefaults] removeObjectForKey:previousName];

// Save the subjects with the new key
[[NSUserDefaults standardUserDefaults] setObject:projectSubjects forKey:newName];
}

- (void)deleteProjects:(NSNotification *)notification {
// Get the current project
NSString *currentProject = [[MyManager sharedManager] projectForDeletion];

[_objects removeObject:currentProject];

[_projectsCollectionView performBatchUpdates:^{

    NSArray *selectedItemsIndexPaths = [_projectsCollectionView indexPathsForSelectedItems];

    // Now delete the items from the collection view.
    [_projectsCollectionView deleteItemsAtIndexPaths:selectedItemsIndexPaths];

} completion:nil];

// Subtract 1 from editedProjects
editedProjects = editedProjects - 1;

// Set deletedProject
deletedProject = currentProject;

// Save the new objects
[[NSUserDefaults standardUserDefaults] setObject:_objects forKey:@"myProjects"];

// Delete the associated subjects if any
[self deleteAssociatedSubjects];

// HERE
[_projectsCollectionView.collectionViewLayout invalidateLayout];
}

-(void)deleteAssociatedSubjects {
// Delete all the subjects inside this project
NSMutableArray *subjects = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:deletedProject]];

int subjectsCount = [subjects count];
for (int i = 0; i < subjectsCount; i++) {
    // Delete all the information associated with this subject

    // Create 3 save keys
    NSString *detailDescriptionKey = [NSString stringWithFormat:@"%@%@", subjects[i], @"-detailDescription"];
    NSString *imageDescriptionKey = [NSString stringWithFormat:@"%@%@", subjects[i], @"-imageDescription"];
    NSString *imageKey = [NSString stringWithFormat:@"%@%@", subjects[i], @"-image"];
    NSString *notesKey = [NSString stringWithFormat:@"%@%@", subjects[i], @"-notes"];

    // Delete the information from NSUserDefaults
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:deletedProject];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:detailDescriptionKey];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:imageDescriptionKey];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:imageKey];
    [[NSUserDefaults standardUserDefaults] removeObjectForKey:notesKey];

    // Reload the UICollectionView
    _objects = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"myProjects"]];
    [_projectsCollectionView.collectionViewLayout invalidateLayout];
}
}

- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_objects = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"myProjects"]];
[_projectsCollectionView.collectionViewLayout invalidateLayout];

// Put the _objects in the singleton objects
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.objects = _objects;
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

#pragma mark - UICollectionView Datasource
// 1
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return _objects.count;
}
// 
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
return 1;
}
// 3
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
ProjectCell *cell = (ProjectCell *)[cv dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
// Get the amount of subjects
        NSMutableArray *subjects = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:_objects[indexPath.row
]]];

// Count the subjects
int subjectsCount = [subjects count];

// Set the text and integer value
cell.projectLabel.text = _objects[indexPath.row];
cell.projectCount.text = [NSString stringWithFormat:@"%i",subjectsCount];

// This helps on the animation
cell.layer.shouldRasterize = YES;

return cell;
}

- (IBAction)createButton:(id)sender {
if (editMode == YES) {
    // Create a new project
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Edit mode is on" message:@"Please exit edit mode before trying to create a project" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
    alert.alertViewStyle = UIAlertViewStyleDefault;
    [alert show];
} else {
    // Create a new project
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"New project" message:@"Enter a name for the project" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Add", nil];
    alert.alertViewStyle = UIAlertViewStylePlainTextInput;
    [alert show];
}
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (deleteAlert == YES) {
    // Set deleteAlert to NO so that we don't mix this UIAlertView
    // with the create project UIAlertView
    deleteAlert = NO;
    // Delete the selected project
} else {
    // The user created a new project, add it
    if (buttonIndex == 1) {
        // Get the input text
        NSString *newProject = [[alertView textFieldAtIndex:0] text];

        // Initialize objects
        if (!_objects) {
            _objects = [[NSMutableArray alloc] init];
        }

        // Add the new project
        if (newProject.length == 0) {
            // No input, tell the user
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Enter a name" message: @"Please enter a name first" delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show];
        } else {
            // Check if the entered subject already exists
            if ([_objects containsObject:newProject]) {
                // The subject already exist, tell the user
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Already exists" message: @"This project already exist, sorry" delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show];
            } else {
                // Ready to add the project, go ahead
                [_objects insertObject:newProject atIndex:0];
                NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
                [_projectsCollectionView insertItemsAtIndexPaths:@[indexPath]];

                [[NSUserDefaults standardUserDefaults] setObject:_objects forKey:@"myProjects"];
            }
        }
    } else {

    }
}
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:     (NSIndexPath *)indexPath
{
// Get the tapped cell
ProjectCell *projectCell = (ProjectCell *)[collectionView cellForItemAtIndexPath:indexPath];

if (editMode == YES) {
    if (editedProjects == 0) {
        // Set deleteAlert to YES so that we don't mix this UIAlertView
        // with the create project UIAlertView
        deleteAlert = YES;

        // Set editMode to YES for the selected cell
        [projectCell editProject];

        // Prepare the project cell
        projectCell.projectTextField.text = projectCell.projectLabel.text;

        // Set the firstProjectsViewController to YES
        // indicating that the next tapped cell will be second in line
        // in case the user decides to edit a new cell without closing this

        if (projectCell.editMode == YES) {
            // Do nothing
        } else if (projectCell.editMode == NO) {
            // Resave projects
            [self resaveProjects:nil];
        }

        // Increase editedProjects with 1
        editedProjects++;
    } else if (editedProjects == 1) {
        // Check if the tapped cell is being edited
        if (projectCell.editMode == YES) {
            // Set deleteAlert to YES so that we don't mix this UIAlertView
            // with the create project UIAlertView
            deleteAlert = YES;

            // Set editMode to YES for the selected cell
            [projectCell editProject];

            // Prepare the project cell
            projectCell.projectTextField.text = projectCell.projectLabel.text;

            // Set the firstProjectsViewController to YES
            // indicating that the next tapped cell will be second in line
            // in case the user decides to edit a new cell without closing this

            if (projectCell.editMode == YES) {
                // Do nothing
            } else if (projectCell.editMode == NO) {
                // Resave projects
                [self resaveProjects:nil];
            }

            // It decreases editedProjects with 1, code is "unlocated"
        } else if (projectCell.editMode == NO) {
            // Tell the user that only 1 project is editable at a time
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Can only edit 1" message:@"You can only edit 1 project at a time" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
            alert.alertViewStyle = UIAlertViewStyleDefault;
            [alert show];
        }
    }
} else {
    // Set the open project
    MyManager *sharedManager = [MyManager sharedManager];
    sharedManager.openProject = nil;
    sharedManager.openProject = @"";
    sharedManager.openProject = _objects[indexPath.item];

    // Open the selected project and dismiss this ViewController
    [self dismissViewControllerAnimated:YES completion:nil];
}
}

- (void)selectedProject {
// The user opened a project - CODE NOT USED ATM
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; // assuming your split view controller in storyboard with name "Main" in project's main bundle
UISplitViewController *splitViewController = (UISplitViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:@"splitVC"];

UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;

// Close the ProjectsViewController and open the selected project
[UIView transitionFromView:[[[[[UIApplication sharedApplication] delegate] window] rootViewController] view]
                    toView:splitViewController.view
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                completion:^(BOOL finished) {
                    [[[[UIApplication sharedApplication] delegate] window] setRootViewController:splitViewController];
                }];
}
- (IBAction)editAction:(id)sender {
// Enter editMode leave editMode
if (editMode == YES) {
    if (editedProjects == 0) {
        // Disable editMode
        editMode = NO;
        [editButton setTitle:@" Edit " forState:UIControlStateNormal];
    } else {
        // The user is currently still editing a project, tell him/her
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Edit mode is on" message: @"It looks like you're still editing a project, please finish editing it by tapping on it before trying to exit edit mode" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
    }
} else {
    // Enable editMode
    editMode = YES;
    [editButton setTitle:@" Finish " forState:UIControlStateNormal];
}
}

/*
#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.
}
*/

@end

这是行为:

  • 删除项目(成功)
  • 尝试添加新项目,不成功(仅在NSUserDefaults中添加,屏幕上没有任何内容)
  • 下一次尝试显示一个奇怪的单元格,其中所有ui元素都可见(通常会隐藏2-3个控件)
  • 下一次尝试完全成功

有没有人知道为什么会发生这种情况以及如何解决这个问题?

非常感谢!

1 个答案:

答案 0 :(得分:0)

所以最后一个else块成功地将newProject添加到NSUserDefaults,但是insertItemsAtIndexPaths实际上没有做任何事情?

使用reloadData的{​​{3}}每个{{0}}似乎与UICollectionView无关。也许您在viewWillAppear中的调用与其他更新_projectsCollectionView的尝试存在冲突...可以将其更改为reloadSectionsinvalidateLayout,以查看是否解决了第一个问题。

对于显示的子视图,如何初始化单元格?这可能是你将editMode设置为&#34; YES&#34;当它应该是&#34; NO。&#34;如果它在出现时出现在单元格本身上的属性&amp;重用它可以使这些视图对另一个项目可见。上面的代码只显示IBAction用于结束编辑,而不是用于启动/切换它,因此很难分辨出这个错误的来源。

您还有以下代码:

-(void)editProject {
if (editMode == YES) {
// disable editMode and update the project cell
editMode = NO;
_projectTextField.hidden = YES;
_editModeLabel.hidden = YES;
_deleteButton.hidden = YES;

// Call editingfinished to save
[self editingFinished:nil]; ...

与调用[self editingFinished:nil];完全相同 因为它包含完全相同的行。如果您在另一种方法中使用类似的if / else检查来开始编辑,则可能是子视图可见性变得棘手的地方。