2015-09-11 12 views
9

Zaimplementowałem system do zmiany kolejności komórek w moich widokach UITableView. Wszystko jest ok, z tym wyjątkiem, że nie mogę ponownie uporządkować komórek do pozycji, która nie jest wyświetlana na ekranie iPhone'a.Automatyczne przewijanie UITableView podczas przeciągania UITableViewCells w aplikacji iOS

Więc wdrożyły warunek, by sprawdzić, czy mam do przewijania

NSArray *indexVisibles = [_tableView indexPathsForVisibleRows]; 
NSInteger indexForObject = [indexVisibles indexOfObject:indexPath]; 
if (indexForObject == NSNotFound){ 
    [_tableView scrollToRowAtIndexPath:indexPath 
              atScrollPosition:UITableViewScrollPositionTop 
                animated:YES]; 
} 

Moim problemem jest to, że animacja nie jest słodkie i czyste.

enter image description here

myślę, że operacja sprawdzania, czy komórka jest wykazały bardzo ogromny dla mojego systemu i zrobić małe opóźnienie kiedy przenieść komórki, ale też nie jestem pewien, dlaczego jest tak przewijania mocno, gdy komórka jest ukryta.

Mam zmienić UITableViewScrollPositionTop na UITableViewScrollPositionMiddle, a teraz jest lepiej, ale prędkość jest bardzo duża, więc zawsze przewijanie idzie na szczyt mojego UITableView.

Chciałbym zrobić to powoli.

Inne stara awaria:

Wariant 1:

[UIView animateWithDuration:0.2 
              animations:^{_tableView.contentOffset = CGPointMake(0.0, _tableView.contentOffset.y - 50);} 
              completion:^(BOOL finished){ }]; 

Ale to ma dwa problemy:

  1. ruch jest nadal ciężki
  2. Podczas przeciągania do pierwszego elementu , ten element jest środkową skórą

enter image description here

Opcja 2:

[UIView animateWithDuration: 1.0 
              animations: ^{ 
               [_tableView scrollToRowAtIndexPath:indexPath 
                    atScrollPosition:UITableViewScrollPositionMiddle 
                      animated:NO]; 
              }completion: ^(BOOL finished){ 

              } 
          ]; 
+1

Sprawdź to repozytorium, czy to działa. https://github.com/hpique/HPReorderTableView –

+0

Wygląda na to, że ma rację, ale ma pewne błędy i nie dotyczy mojego projektu. Ale studiuję twój kod, aby znaleźć błąd i naprawić je – EnriMR

Odpowiedz

11

Rozwiązałem mój problem za pomocą bardzo pięknego rozwiązania, więc wyjaśnię w trzech prostych krokach, jak to zrobić.

muszę użyć trochę inspiracji z https://github.com/hpique/HPReorderTableView i udostępniać je we własnym repozytorium https://github.com/enrimr/EMRReorderTableCells

A. Zarządzanie gestureRecognition

longPressGestureRecognized:

- (IBAction)longPressGestureRecognized:(id)sender { 

    _reorderGestureRecognizer = (UILongPressGestureRecognizer *)sender; 

    CGPoint location = [_reorderGestureRecognizer locationInView:_tableView]; 
    NSIndexPath *indexPath = [self getCellIndexPathWithPoint:location]; 

    UIGestureRecognizerState state = _reorderGestureRecognizer.state; 
    switch (state) { 
     case UIGestureRecognizerStateBegan: { 

      NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:location]; 
      if (indexPath == nil) 
      { 
       [self gestureRecognizerCancel:_reorderGestureRecognizer]; 
       break; 
      } 

      // For scrolling while dragging 
      _scrollDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scrollTableWithCell:)]; 
      [_scrollDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 

      // Check for the right indexes (between margins of offset 
      if (indexPath.row >=_elementsOffset && indexPath.row < [_elements count]+_elementsOffset){ 

       if (indexPath) { 
        sourceIndexPath = indexPath; 

        id sourceElement = [_elements objectAtIndex:sourceIndexPath.row-_elementsOffset]; 

        snapshot = [self createSnapshotForCellAtIndexPath:indexPath withPosition:location]; 
       } 
      } else { 
       sourceIndexPath = nil; 
       snapshot = nil; 
      } 
      break; 
     } 

     case UIGestureRecognizerStateChanged: { 

      [self calculateScroll:_reorderGestureRecognizer]; 

      if (sourceIndexPath != nil && indexPath.row >=_elementsOffset && indexPath.row < [_elements count]+_elementsOffset){ 
       [self updateSnapshotWithPosition:location]; 

       // Is destination valid and is it different from source? 
       if (indexPath && ![indexPath isEqual:sourceIndexPath]) { 
        if (indexPath.row - sourceIndexPath.row <= 1){ 

         id sourceElement = [_elements objectAtIndex:sourceIndexPath.row-_elementsOffset]; 
         id targetElement = [_elements objectAtIndex:indexPath.row-_elementsOffset]; 

         sourceIndexPath = [self exchangeElement:sourceElement byElement:targetElement]; 
        } 

       } 
      } 
      break; 
     } 

     case UIGestureRecognizerStateEnded: 
     { 
      // For scrolling while dragging 
      [_scrollDisplayLink invalidate]; 
      _scrollDisplayLink = nil; 
      _scrollRate = 0; 


      // Check if it is the last element 
      if (sourceIndexPath != nil){ 
       id element; 
       if (indexPath.row <=_elementsOffset){ 
        element = [_elements firstObject]; 
       } else if (indexPath.row > [_elements count]-1+_elementsOffset){ 
        element = [_elements lastObject]; 
       } else { 
        element = [_elements objectAtIndex:indexPath.row-_elementsOffset]; 
       } 
      } 

     } 

     default: { 
      // Clean up. 
      [self deleteSnapshotForRowAtIndexPath:sourceIndexPath]; 

      [appDelegate startTimer]; 

      break; 
     } 
    } 
} 

gestureRecognizer Anuluj:

Służy do anulowania rozpoznawania gestów, aby zakończyć akcję zmiany kolejności.

-(void) gestureRecognizerCancel:(UIGestureRecognizer *) gestureRecognizer 
{ // See: http://stackoverflow.com/a/4167471/143378 
    gestureRecognizer.enabled = NO; 
    gestureRecognizer.enabled = YES; 
} 

scrollTableWithCell:

Metoda nazywa się to zrobić ruch przewijania, gdy jesteś w granicach tabeli (w górę iw dół)

- (void)scrollTableWithCell:(NSTimer *)timer 
{ 
    UILongPressGestureRecognizer *gesture = _reorderGestureRecognizer; 
    const CGPoint location = [gesture locationInView:_tableView]; 

    CGPoint currentOffset = _tableView.contentOffset; 
    CGPoint newOffset = CGPointMake(currentOffset.x, currentOffset.y + _scrollRate * 10); 

    if (newOffset.y < -_tableView.contentInset.top) 
    { 
     newOffset.y = -_tableView.contentInset.top; 
    } 
    else if (_tableView.contentSize.height + _tableView.contentInset.bottom < _tableView.frame.size.height) 
    { 
     newOffset = currentOffset; 
    } 
    else if (newOffset.y > (_tableView.contentSize.height + _tableView.contentInset.bottom) - _tableView.frame.size.height) 
    { 
     newOffset.y = (_tableView.contentSize.height + _tableView.contentInset.bottom) - _tableView.frame.size.height; 
    } 

    [_tableView setContentOffset:newOffset]; 

    if (location.y >= 0 && location.y <= _tableView.contentSize.height + 50) 
    { 

     [self updateSnapshotWithPosition:location]; 
     NSIndexPath *indexPath = [self getCellIndexPathWithPoint:location]; 

     // CHeck if element is between offset limits. 
     if (![indexPath isEqual:sourceIndexPath] && 
      indexPath.row >= _elementsOffset && 
      indexPath.row - _elementsOffset < [_elements count] && 
      sourceIndexPath.row >= _elementsOffset && 
      sourceIndexPath.row - _elementsOffset < [_elements count]) 
     { 
      id sourceElement = [_elements objectAtIndex:sourceIndexPath.row-_elementsOffset]; 
      id targetElement = [_elements objectAtIndex:indexPath.row-_elementsOffset]; 
      [self exchangeElement:sourceElement byElement:targetElement]; 
      sourceIndexPath = indexPath; 
     } 
    } 
} 

B. Zarządzanie migawkami

createSnapshotForCellAtIndexPath: withPosition

metoda, która tworzy migawkę (kopia obrazu) komórki jesteś w ruchu

-(UIView *)createSnapshotForCellAtIndexPath:(NSIndexPath *)indexPath withPosition:(CGPoint)location{ 
    UITableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath]; 

    // Take a snapshot of the selected row using helper method. 
    snapshot = [self customSnapshoFromView:cell]; 

    // Add the snapshot as subview, centered at cell's center... 
    __block CGPoint center = cell.center; 
    snapshot.center = center; 
    snapshot.alpha = 0.0; 

    [_tableView addSubview:snapshot]; 
    [UIView animateWithDuration:0.25 animations:^{ 

     // Offset for gesture location. 
     center.y = location.y; 
     snapshot.center = center; 
     snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05); 
     snapshot.alpha = 0.98; 
     cell.alpha = 0.0; 

    } completion:^(BOOL finished) { 

     cell.hidden = YES; 
    }]; 

    return snapshot; 
} 

customSnapshoFromView:

Zwraca dostosowany migawkę dany widok. */

- (UIView *)customSnapshoFromView:(UIView *)inputView { 

    // Make an image from the input view. 
    UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0); 
    [inputView.layer renderInContext:UIGraphicsGetCurrentContext()]; 
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    // Create an image view. 
    snapshot = [[UIImageView alloc] initWithImage:image]; 
    snapshot.layer.masksToBounds = NO; 
    snapshot.layer.cornerRadius = 0.0; 
    snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0); 
    snapshot.layer.shadowRadius = 5.0; 
    snapshot.layer.shadowOpacity = 0.4; 

    return snapshot; 
} 

updateSnapshotWithPosition:

Biorąc pod uwagę CGPoint, zmienia pozycję migawki, aby pokazać komórkę jesteś zmierza we właściwym miejscu _tableView

-(void)updateSnapshotWithPosition:(CGPoint)location{ 
    CGPoint center = snapshot.center; 
    center.y = location.y; 
    snapshot.center = center; 
} 

deleteSnapshotForRowAtIndexPath:

Podczas przeciągania wykończeń, trzeba usunąć migawkę z _tableView

-(void)deleteSnapshotForRowAtIndexPath:(NSIndexPath *)sourceIndexPath{ 
    UITableViewCell *cell = [_tableView cellForRowAtIndexPath:sourceIndexPath]; 
    cell.hidden = NO; 
    cell.alpha = 0.0; 

    [UIView animateWithDuration:0.25 animations:^{ 

     snapshot.center = cell.center; 
     snapshot.transform = CGAffineTransformIdentity; 
     snapshot.alpha = 0.0; 
     cell.alpha = 1.0; 

    } completion:^(BOOL finished) { 
     [snapshot removeFromSuperview]; 
    }]; 
} 

calculateScroll

-(void)calculateScroll:(UIGestureRecognizer *)gestureRecognizer{ 

    const CGPoint location = [gestureRecognizer locationInView:_tableView]; 

    CGRect rect = _tableView.bounds; 
    // adjust rect for content inset as we will use it below for calculating scroll zones 
    rect.size.height -= _tableView.contentInset.top; 

    //[self updateCurrentLocation:gestureRecognizer]; 

    // tell us if we should scroll and which direction 
    CGFloat scrollZoneHeight = rect.size.height/6; 
    CGFloat bottomScrollBeginning = _tableView.contentOffset.y + _tableView.contentInset.top + rect.size.height - scrollZoneHeight; 
    CGFloat topScrollBeginning = _tableView.contentOffset.y + _tableView.contentInset.top + scrollZoneHeight; 

    // we're in the bottom zone 
    if (location.y >= bottomScrollBeginning) 
    { 
     _scrollRate = (location.y - bottomScrollBeginning)/scrollZoneHeight; 
    } 
    // we're in the top zone 
    else if (location.y <= topScrollBeginning) 
    { 
     _scrollRate = (location.y - topScrollBeginning)/scrollZoneHeight; 
    } 
    else 
    { 
     _scrollRate = 0; 
    } 

} 

C.Jak z niego korzystać

W swojej metodzie init, przypisz program do rozpoznawania gestów do widoku tabeli. Przypisać jako działanie metody longPressGestureRecognized: następująco:

_reorderGestureRecognizer = [[UILongPressGestureRecognizer alloc] 
               initWithTarget:self action:@selector(longPressGestureRecognized:)]; 

    [_tableView addGestureRecognizer:_reorderGestureRecognizer]; 

Zadeklaruj zmienne trzeba będzie użyć powyższy kod wyjaśnił

@implementation YourClassName{ 

    CADisplayLink *_scrollDisplayLink; 
    CGFloat _scrollRate; 
    UIView *snapshot; // A snapshot of the row user is moving. 
    NSIndexPath *sourceIndexPath; // Initial index path, where gesture begins. 
} 

I to wszystko trzeba będzie rozwiązać problem miałem.

+0

Czy możesz udostępnić cały kod źródłowy kontrolera widoku, czy możesz pokazać, co to jest "_elementsOffset" (jak jest obliczany) i inne metody pomocnicze, np. "calculateScroll" – Karaban

+0

elementsOffset jest opcjonalny, możesz go użyć, jeśli tabela zawiera elementy w górnej części tabeli, które chcesz zablokować. Mam udział w tym repozytorium moja klasa https://github.com/enrimr/EMRReorderTableCells, jeśli masz jakieś pytanie, nie wahaj się zapytać mnie;) Mam nadzieję, że może ci pomóc! – EnriMR

+0

wielkie dzięki! – Karaban

1

Ja również poszukuje sposobu, aby uczynić komórki Tableview przez długi je naciskając. I znalazłem repozytorium do tego celu. Jeśli nie masz nic przeciwko umieszczeniu biblioteki innej firmy w swoim projekcie, sprawdź to! :)

LPRTableView

Osobiście używam tego kodu do mojego app Cheetah Note. i działają jak urok! Wysoce polecany!

+1

Pracuję z Objective-C, ale w opisie tego repozytorium, znalazłem, że https://github.com/bvogelzang/BVReorderTableView .I'm przetestować to – EnriMR

+0

Tak! Powodzenia! :) –

+1

Nie mogę skompilować tego projektu. – EnriMR

0

używając opcji jedną szansę, aby przejść do przeciągnięty pozycji komórkowy - 1, gdy przeciągany komórka jest na szczycie:

NSArray *indexVisibles = [_tableView indexPathsForVisibleRows]; 
NSInteger indexForObject = [indexVisibles indexOfObject:indexPath]; 
if (indexForObject == 0){ 
    [_tableView scrollToRowAtIndexPath:indexPathForCellBefore 
              atScrollPosition:UITableViewScrollPositionTop 
                animated:YES]; 
} 

płynnie przewijać tableview użyć następującego fragmentu:

[UIView animateWithDuration: 1.0 animations: ^{ 
    [_tableView scrollToRowAtIndexPath:indexPathForCellBefore atScrollPosition:UITableViewScrollPositionTop animated:NO]; 
} completion: ^(BOOL finished){ 
}]; 

Oczywiście musisz to zmodyfikować, aby działało, gdy przewiniesz w dół.

+0

To mi nie pomaga, ponieważ animacja dla 'scrollToRowAtIndexPath: atScrollPosition: Animated' jest bardzo silna, chciałbym przewinąć UITableView płynnie – EnriMR

+0

To prawda, ale czy próbowałeś? – hasan83

+1

Tak, rozwiązuje jeden z moich problemów, czyli jak utrzymać komórkę, którą przeciągasz w pozycji 0, kiedy przeciągasz ją do góry, ale ruch nie jest płynny. – EnriMR

Powiązane problemy