2014-10-14 1 views
0

NSArrayNSDictionary입니다 (사전에는 몇 가지 키 값 쌍이 들어 있습니다). 내 이야기 ​​게시판에는 여러 개의 UISegmentedControl이 있으며 선택한 이벤트 중에 사전의 배열을 필터링합니다 (멋진 작품). 선택된 상태와 인스턴스 변수를 SegmentedControls에 저장하여 이전에 선택했는지 또는 처음 선택했는지 알 수 있습니다.여러 개의 UIControls를 사용하여 NSDictionaries의 NSArray를 필터링하십시오.

문제는 각 UISegmentControl에 의해 생성 된 여러 조건자를 사용하여 사전 배열을 필터링하려고하는 것입니다. 예를 들어 SegmentControl 1을 클릭하면 그에 따라 배열이 필터링됩니다. SegmentControl 2를 클릭하면 현재 결과가 필터링됩니다. 그러나 SegmentControl 1에서 다른 값을 선택하면 모든 값이 원래 필터링되어 있기 때문에 아무것도 반환하지 않습니다. 이 세그먼트 중 5 개가 있으며 사실 viewDidLoad에서 생성 된 필터링되지 않은 세트를 필터링 할 수 있어야합니다 (필터링되지 않은 데이터는 JSON 소스에서 가져오고 앱이로드 될 때 한 번 저장됩니다). JSON 소스에서 여러 번 가져 오는 것을 방지).

이 필터 문제를 해결하기 위해 몹시 괴롭 히고 있습니다. 나는 이것이 내 하루 종일을 보면서 내 머리가 으르렁 거리거나 objc (나는 많은 oop 프로그램 경험을 가지고있다. 그러나 이것은 나의 첫 번째 목표 C app이다)를 가진 나의 초보자 능력이라면 그것이 확실한 지 모른다. 나는 원래 필터링되지 않은 배열을 받아들이는 함수/메소드를 만들려고 생각했고 필터링 된 데이터를 반환하는 모든 선택된 필터 값을 생각했지만 일부 필터 필드가없는 경우 일어날 일을 머리로 감쌀 수 없었다. 이것을 접근하는 가장 좋은 방법은 무엇입니까? 여기에 몇 가지 코드가 있습니다 :

@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 

내 내 JSON 소스에서 데이터를 myArrayOfDictionaries 작성하고 있습니다 viewDidLoad().

필터링 할 때 사용하는 각 UISegmentView에 대해 IBOutlet 이벤트가 있습니다. 내 UISegmentControl의의

/// 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]; 

} 

모든는 다음과 같습니다 : 그들은 모두 거의 같은 마이너스 이름 때문에 나는 당신에게 하나를 보여 드리겠습니다. 각 SegmentControl 이벤트를 호출하여 필터링 된 배열을 반환하는 함수를 작성하기 시작했으나 이것이 최선의 방법인지는 확실하지 않습니다. 여기에 내가 지금까지 가지고있는 것이있다. 나는 술어 또는 단지 문자열을 전달해야하는지 잘 모르겠다.

- (NSArray*)filteredResults:(NSArray*)unfilteredArray forSegment1:(NSPredicate*)segment1 forSegment2:(NSPredicate*)segment2 forSegment3:(NSPredicate*)segment3 forSegment4:(NSPredicate*)segment4; 

나는 올바른 방향으로 가고 있습니까? 그것은 모두 정말로 혼란스러워졌습니다. Segment1을 선택한 다음 Segment2를 선택하면 올바르게 필터링된다는 것을 알고 있습니다. 그러나 이미 선택한 세그먼트의 값을 변경하는 즉시 세그먼트 값이 첫 번째 선택 항목에 의해 필터링 되었기 때문에 아무 것도 반환하지 않습니다. 이전에 세그먼트 컨트롤을 선택했을 때 필 터링 된 것과 필터링되지 않은 배열을 비교할 수 있습니까? 나는 이것이 정말로 합리적이되기를 바란다. 보고와 독서에 감사드립니다.

+0

사용 해보십시오 : NSArray * tmpArray = [[NSArray alloc] initWithArray : unfilteredArrayOfDictionaries]; 대신에 : NSArray * tmpArray = [[NSArray alloc] initWithArray : filteredArrayOfDictionaries]; –

+0

하지만 선택한 다른 세그먼트 컨트롤에서 필터링이 제거됩니다. 맞습니까? – drpcken

+0

죄송합니다. 명확하게 알 수 없습니다 :( –

답변

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]; 

    [email protected][@"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; 
} 
+0

각 segmentedcontrol에 대한 태그를 설정한다고 말하면'predicateKeys' 배열에 나타나는 순서대로 설정 하시겠습니까? 그런데 고맙습니다! – drpcken

+0

그 부분을 알았습니다. out!하지만 여전히 필터링 된 결과를 필터링 할 때 이미 선택되어있는 분할 된 뷰를 변경합니다.이 줄은 다음과 같을 것이라고 생각합니다 : 'tempArray = [tempArray filteredArrayUsingPredicate : predicate];'. 필터링 된 값을 가져 와서 값을 변경할 때 다시 필터링합니다. – drpcken

+0

필터링되지 않은 배열을 모두 다시로드 할 수 있도록 세그먼트가 변경되었는지 확인해야한다고 생각합니다. – drpcken

2

이것은 MVC 패러다임이 빠르게 "대규모보기 컨트롤러"아키텍처가 될 수있는 완벽한 예입니다.

귀하의보기 컨트롤러는 이제 5 개의 개별 데이터 소스를 관리하고 그 사이의 상태를 관리해야하는 부담이 있습니다. 가난한 사람!

당신이해야 할 일은 집계 된 데이터 소스를 구현하는 것입니다. 구체적으로는 세그먼트 화 된 데이터 소스를 구현하는 것입니다. 이를 통해 모든 데이터 소스 로직을 속한 모델 오브젝트로 추상화 할 수 있습니다.

세그먼트 화 된 데이터 소스는 적절한 데이터 소스를 판매 할뿐만 아니라 하위 데이터 소스를 관리합니다. 이러한 유형의 디자인을 사용하면 코드를 변경하여 더 많은 배열을 포함하거나 적절하게 배열을 제거하는 것이 훨씬 쉬워집니다.

독자적인 책임 원칙은 코드 냄새의 징조를 나타내는 코드를 발견하면 매우 매력적이어야합니다.

계속 하시겠습니까? 그런 다음 고급 콜렉션 뷰 레이아웃에 대한 WWDC의 연설을 지켜 보면서 View Controller의 복잡성을 줄이고 재사용 가능한 데이터 소스를 구축하는 방법에 대해 정확히 의논합니다. 모든 링크에 대해서는 this blog post을 참조하십시오.

+1

나는이 대답을 좋아한다. 처음에는 모든 것을 추상적으로 유지하려고했지만 viewDidLoad에 던져서 '문제'를 해결하는 것이 더 쉬웠습니다.그래서 DataSource를 말할 때 별도의 클래스를 의미합니까? 정말 고맙습니다! – drpcken

+0

@drpcken 그것을 듣기 좋게! 우리는 모두 미래에 우리가 해결할 문제를 만들지 만 유죄입니다.) 그러나 중요한 것은 여러분의 간단한 해결책이 더 큰 문제를 만들 때를 인식 할 수 있다는 것입니다 :) 나는 분리 된 클래스를 의미했습니다. 예 :) –

관련 문제