我有一个NSArray
NSDictionary
个(字典包含几个键值对)。在我的故事板上,我有几个UISegmentedControl
s,在他们选择的活动中,我正在过滤掉我的一系列词典(可爱的作品)。我将这些SegmentedControls的选定状态和值存储在实例变量中,以便我可以判断它们之前是先选过还是第一次被选中。
我遇到的问题是尝试使用每个UISegmentControl
创建的多个谓词来过滤这个词典数组。例如,如果我单击SegmentControl 1它会相应地过滤数组,如果我单击SegmentControl 2它将获取当前结果并过滤这些结果。但是,如果我从SegmentControl 1中选择另一个值,则不返回任何值,因为所有这些值最初都已过滤掉。由于我有5个这样的段,我需要确保它可以过滤我在viewDidLoad
上生成的未经过滤的集合(我的未经过滤的数据来自JSON源并在存储时存储一次),这更加突出了这一点。应用程序加载以防止它多次从JSON源中拉出来。)
我正在努力解决此过滤器问题。我不确定是不是因为我的大脑一整天都在看这个或者是否是objc的新手能力(我有很多oop程序经验,但这是我的第一个目标c应用程序)。我想最初制作一个接受未过滤数组的函数/方法,以及所有选定的过滤器值来返回过滤后的数据,但我无法理解如果某些过滤器字段为零则会发生什么。解决这个问题的最佳方法是什么?这是一些代码:
@interface ViewController ()
{
//Checks for UISegments
Boolean isSegment1Selected;
Boolean isSegment2Selected;
Boolean isSegment3Selected;
Boolean isSegment4Selected;
//Instance FilterValues
NSString *segment1Value;
NSString *segment2Value;
NSString *segment3Value;
NSString *segment4Value;
//Create arrays for dictionaries
NSMutableArray *myArrayOfDictionaries;
NSMutableArray *unfilteredArrayOfDictionaries;
NSMutableArray *filteredArrayOfDictionaries;
}
@end
在我的viewDidLoad()
上,我正在使用来自我的JSON来源的数据填充myArrayOfDictionaries
。
我使用过滤的每个UISegmentView
都有IBOutlet事件。我会告诉你一个,因为他们几乎都是相同的减去他们的名字:
/// UISegmentedControl Filters ///
- (IBAction)segment1:(UISegmentedControl *)sender {
// NSLog(isSegment1Selected ? @"YES" : @"NO");
//Haven't used this if/else yet but guessing it will be part of the solution
if (segment1Value == nil)
{
NSLog(@"Nothing");
}
else
{
NSLog(@"Something");
}
//Grab value of SegmentControl into instance variable and strip additional characters not needed from the end
segment1Value = [[sender titleForSegmentAtIndex:sender.selectedSegmentIndex] substringToIndex:[sender titleForSegmentAtIndex:sender.selectedSegmentIndex].length -1 ];
//Create predicate filter with key and value
NSPredicate *predicateForSegment1 = [NSPredicate predicateWithFormat:@"(%K == %@)", segment1_key, segment1Value];
//Check if filteredArray already has objects
if ([filteredArrayOfDictionaries count] == 0)
{
//If filteredArray is empty, fill it with the filtered data using predicate from selected Segmented index control. Then clear the array and copy filtered array to filtered array
filteredArrayOfDictionaries = [[unfilteredArrayOfDictionaries filteredArrayUsingPredicate:predicateForSegment1] mutableCopy];
}
else
{
//If filtered array is full, empty it first then fill it with new filtered values.
NSArray *tmpArray = [[NSArray alloc] initWithArray:filteredArrayOfDictionaries];
[filteredArrayOfDictionaries removeAllObjects];
filteredArrayOfDictionaries = [[tmpArray filteredArrayUsingPredicate:predicateForImpellerDiameter] mutableCopy];
}
[myArrayOfDictionaries removeAllObjects];
[myArrayOfDictionaries addObjectsFromArray:filteredArrayOfDictionaries];
[tableData reloadData];
}
我UISegmentControl
的所有内容都是这样的。我开始编写一个函数来调用每个SegmentControl事件以返回已过滤的数组,但我不确定这是否也是最好的方法。这是我到目前为止所做的,我不确定是否应该传递谓词或字符串。
- (NSArray*)filteredResults:(NSArray*)unfilteredArray forSegment1:(NSPredicate*)segment1 forSegment2:(NSPredicate*)segment2 forSegment3:(NSPredicate*)segment3 forSegment4:(NSPredicate*)segment4;
我是以正确的方式来做这件事的吗?这一切都变得非常混乱。我知道当我选择Segment1然后选择Segment2时,它会正确过滤。但是一旦我更改了已经选择的片段的值,它就不会返回任何内容,因为第一个选择已经过滤了Segments值。在先前选择了段控件时,是否可以比较我的已过滤和未过滤的数组?我真的希望这是有道理的。感谢您查看和阅读。
答案 0 :(得分:2)
这是MVC范例如何迅速成为“大规模视图控制器”的完美示例。架构。
您的View Controller现在负责维护5个独立的数据源并管理它们之间的状态。可怜的家伙!
您应该考虑的是实现聚合数据源,特别是在您的情况下是分段数据源。这允许您将所有数据源逻辑抽象为它所属的模型对象。
分段数据源负责管理子数据源以及出售相应的选定数据源。这种类型的设计使您可以更加轻松地更改代码以包含更多数组或根据需要删除数组。
单一责任原则是你的朋友,抽象应该非常有吸引力,因为你注意到你的代码表现出代码味道的迹象。
想继续下去吗?然后,我强烈建议观看今年WWDC关于高级集合视图布局的演讲,他们将详细讨论如何降低View Controller的复杂性并构建可重用的数据源。有关所有链接,请参阅this blog post
答案 1 :(得分:1)
每当其中一个段发生更改时,您需要重建已过滤的阵列。我会用这样的东西
@interface ViewController ()
{
//Create arrays for dictionaries
@property (strong,nonatomic) NSMutableArray *myArrayOfDictionaries;
@property (strong,nonatomic) NSMutableArray *filteredArrayOfDictionaries;
//Array for storage of predicates
@property (strong,nonatomic) NSMutableArray *predicates;
@property (strong,nonatomic) NSArray *predicateKeys;
}
@end
-(void) viewDidLoad {
[super viewDidLoad];
self.predicateKeys=@[@"key1",@"key2",@"key3",@"key4",@"key4"];
self.predicates=[NSMutableArray new];
for (int i=0;i<self.predicateKeys.count;i++) {
[self.predicates addObject:[NSNull null]];
}
[self setupFilteredArray];
}
// Set the tag for each segmented controller to it's "number" - 0 to 5 and set this method as the action handler for all of the segmented controls
- (IBAction)segmentChanged:(UISegmentedControl *)sender {
NSInteger segmentNumber=sender.tag;
//Grab value of SegmentControl into instance variable and strip additional characters not needed from the end
segmentValue = [[sender titleForSegmentAtIndex:sender.selectedSegmentIndex] substringToIndex:[sender titleForSegmentAtIndex:sender.selectedSegmentIndex].length -1 ];
if ([segmentValue isEqualToString:@""]) {
[self.predicates replaceObjectAtIndex:segmentNumber withObject:[NSNull null]];
}
else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(%K == %@)",self.predicateKeys[segmentNumber], segmentValue];
[self.predicates replaceObjectAtIndex:segmentNumber withObject:predicate];
}
[self setupFilteredArray];
}
-(void) setupFilteredArray {
NSArray *tempArray=[NSArray arrayWithArray:self.myArrayOfDictionaries];
for (int i=0;i<self.predicates.count;i++) {
if (!([self.predicates[i] isEqual:[NSNull null]])) {
NSPredicate *predicate=(NSPredicate *)self.predicates[i];
tempArray=[tempArray filteredArrayUsingPredicate:predicate];
}
}
self.filteredArray=tempArray;
}