Dodawanie powiększenia pinch do UICollectionView
Zamierzam opisać efekt, który chcę osiągnąć, a następnie podam szczegóły dotyczące tego, jak obecnie próbuję go wdrożyć i co jest nie tak z jego zachowaniem. Wspomnę również o innym podejściu, na które patrzyłem, ale w ogóle nie mogłem wykonać pracy.
Najbardziej odpowiedni kod znajduje się w dolnej części pytania w celu szybkiego dostępu. MożeszPobierz zamek źródłowy lub pobierz projekt jakoRepozytorium Mercurial w BitBucket. Projekt zawiera teraz poprawki z odpowiedzi poniżej. Jeśli chcesz, aby pierwotnie udostępniona została zepsuta wersja, jest ona oznaczona tagiem„wersja początkowa z błędem”
Projekt jest minimalnym dowodem na koncepcję / skok do oceny, czy efekt jest opłacalny, więc jest dość lekki i prosty!
Pożądany efektAplikacja wyświetli dużą liczbę dyskretnych wierszy informacji, które tworzą pionową tabelę. Tabela będzie przewijana pionowo przez użytkownika. Jest to standardowe zachowanie zUITableView
i możesz użyć aUICollectionView
zbyt. Jednak aplikacja musi również obsługiwać skalowanie szczypiące. Kiedy ścisniesz powiększenie na stole,wszystko linii powinno się zgnieść razem. Jak się rozciągasz,wszystko linii powinno się rozdzielić.
W moim dowodzie koncepcji poszczególne komórki nie są zmieniane, są po prostu umieszczane bliżej siebie lub dalej od siebie. Jest to celowe: nie wierzę, że ma to kluczowe znaczenie dla potwierdzenia wykonalności pomysłu.
Oto zrzut ekranu pokazujący, jak bieżąca aplikacja wygląda na powiększoną i pomniejszoną:
Aktualne wdrożenie
UżywamUICollectionView
ze zwyczajemUICollectionViewLayout
podklasa. Układ ustawiaUICollectionViewCells
w ładnej, chwiejnej fali sinusoidalnej na środku ekranu. KażdyUICollectionViewCell
jest tylko pojemnikiem naUILabel
trzymającindexPath
rząd.
TheUICollectionViewLayout
podklasa ma parametr umożliwiający ustawienie odstępu pionowego między każdą komórką, którą opisujeUICollectionView
a dostosowanie to umożliwia zgniatanie lub rozciąganie stołu w zależności od potrzeb.
MójUICollectionViewController
podklasa maUIPinchGestureRecognizer
. Gdy urządzenie rozpoznające wykryje zmiany skali, pionowy odstęp komórek wUICollectionView
układ strony jest odpowiednio zmieniany.
Bez dalszych rozważań skalowanie wystąpiłoby z góry zawartości, a nie z centrum gestu dotykowego. TheUICollectionView
jestcontentOffset
właściwość jest dostosowywana podczas szczypania, aby zapewnić tę funkcję.
Rozpoznawacz gestów musi również uwzględniać opory, które występują podczas ściskania. Jest to również obsługiwane przez zmianęUICollectionView
jestcontentOffset
. Dodatkowy kod umożliwia zmianę punktu środkowego gestu dotykowego w miarę dodawania palców do gestu.
Zauważ, żeUICollectionView
, będąc aUIScrollView
podklasa, ma swoją własnąUIPanGestureRecognizer
który współdziała zUIPinchGestureRecogniser
dodane przeze mnie. Nie jestem pewien, czy powoduje to problem, czy nie.
Dodałem kod, aby wyłączyćUICollectionView
jest wbudowany w przewijanie podczas mojego gestu szczypania, ale to nie wydaje się mieć większego znaczenia. Próbowałem użyćgestureRecognizer:shouldRequireFailureOfGestureRecognizer:
zrobić mojeUIPinchGestureRecognizer
zawieść wbudowanegoUIPanGestureRecognizer
, ale zamiast tego wydawało się, że w ogóle nie działa mój szczypta. Nie wiem, czy to ja jestem głupi, czy błąd w iOS.
Jak wspomniano wcześniej, bieżąceUICollectionViewCell
s nie są zmieniane. Zostały po prostu zmienione. To jest zamierzone. Nie sądzę, aby to ważne dla walidacji tej koncepcji.
Działające bity działają całkiem dobrze. Możesz przeciągać stół w górę iw dół. Podczas przeciągania możesz dodać palec i rozpocząć szczyptę, a następnie zwolnić palec i kontynuować przeciąganie, a następnie dodać, uszczypnąć itp. Wszystko jest całkiem gładkie. Na oryginalnym telefonie iPhone 5 płynnie obsługuje szczyptę i panoramowanie z> 200 widokami na ekranie.
Co nie działa 1Jeśli spróbujesz uszczypnąć i zsunąć się, gdy górna lub dolna część widoku jest na ekranie, to wszystko jest nieco szalone.
W przypadku zwojów widok może przeciągać się, tak aby był wyciągany poza widoczną zawartość (czego chcę, ponieważ jest to standardowe zachowanie dla listy danych na iOS).Jednak w przypadku zmiany skali widok jest przyciągany do tyłu, dzięki czemu treść zostaje zaciśnięta na ekranie (nie chcę tego robić).Te dwie walki walczą ze sobą podczas gestu szczypania, co sprawia, że zawartość gwałtownie migocze w górę iw dół (czego zdecydowanie nie chcę!).
Co nie działa 2TheUICollectionView
domyślne przewijanie ma opóźnienie, jeśli puścisz podczas przewijania, a także płynnie odbija zawartość po przewinięciu poza nią. Obecnie nie są one obsługiwane.
UICollectionView
, będąc aUIScrollView
powinien mieć wbudowanyUIPinchGestureRecogniser
jeśli jest poprawnie skonfigurowany do obsługi powiększania. Zastanawiałem się, czy udałoby mi się to wykorzystać zamiast mieć własneUIPinchGestureRecogniser
. Próbowałem to ustawić, ustawiając skale min i max oraz dodając obsługę szczypania kontrolera. Jednak tak naprawdę nie rozumiem, co powinienem powrócić z mojej implementacjiviewForZoomingInScrollView:
, więc tworzę tylko fikcyjny widok[[UIView alloc] initWithFrame: [[self collectionView] bounds]]
. Sprawia, że widok przewijania jest zwinięty w pojedynczą linię, co nie jest tym, czego szukam!
To długie pytanie, więc dziękuję za przeczytanie. Jeszcze bardziej dziękuję, jeśli możesz pomóc w odpowiedzi. Przepraszam, jeśli wiele z tego, co powiedziałem lub dodałem, jest nieistotne!
Kod kontrolera widoku// STViewController.m
#import "STViewController.h"
#import "STDataColumnsCollectionViewLayout.h"
#import "STCollectionViewLabelCell.h"
@interface STViewController () <UIGestureRecognizerDelegate>
@property (nonatomic, assign) CGFloat pinchStartVerticalPeriod;
@property (nonatomic, assign) CGFloat pinchNormalisedVerticalPosition;
@property (nonatomic, assign) NSInteger pinchTouchCount;
-(void) handlePinch: (UIPinchGestureRecognizer *) pinchRecogniser;
@end
@implementation STViewController
-(void) viewDidLoad
{
[[self collectionView] registerClass: [STCollectionViewLabelCell class] forCellWithReuseIdentifier: [STCollectionViewLabelCell className]];
UICollectionView *const collectionView = [self collectionView];
[collectionView setAllowsSelection: NO];
[_pinchRecogniser addTarget: self action: @selector(handlePinch:)];
[_pinchRecogniser setDelegate: self];
[_pinchRecogniser setCancelsTouchesInView:YES];
[[self view] addGestureRecognizer: _pinchRecogniser];
}
#pragma mark -
-(NSInteger) collectionView: (UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return 800;
}
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
STCollectionViewLabelCell *const cell = [[self collectionView] dequeueReusableCellWithReuseIdentifier: [STCollectionViewLabelCell className] forIndexPath: indexPath];
[[cell label] setText: [NSString stringWithFormat: @"%d", [indexPath row]]];
return cell;
}
#pragma mark -
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
#pragma mark -
-(void) handlePinch: (UIPinchGestureRecognizer *) pinchRecogniser
{
UICollectionView *const collectionView = [self collectionView];
STDataColumnsCollectionViewLayout *const layout = (STDataColumnsCollectionViewLayout *)[self collectionViewLayout];
if(([pinchRecogniser state] == UIGestureRecognizerStateBegan) || ([pinchRecogniser numberOfTouches] != _pinchTouchCount))
{
const CGFloat normalisedY = [pinchRecogniser locationInView: collectionView].y / [layout collectionViewContentSize].height;
_pinchNormalisedVerticalPosition = normalisedY;
_pinchTouchCount = [pinchRecogniser numberOfTouches];
}
switch ([pinchRecogniser state])
{
case UIGestureRecognizerStateBegan:
{
NSLog(@"Began");
_pinchStartVerticalPeriod = [layout verticalPeriod];
[collectionView setScrollEnabled: NO];
break;
}
case UIGestureRecognizerStateChanged:
{
NSLog(@"Changed");
STDataColumnsCollectionViewLayout *const layout = (STDataColumnsCollectionViewLayout *)[self collectionViewLayout];
const CGFloat newVerticalPeriod = _pinchStartVerticalPeriod * [pinchRecogniser scale];
[layout setVerticalPeriod: newVerticalPeriod];
[[self collectionViewLayout] invalidateLayout];
const CGPoint dragCenter = [pinchRecogniser locationInView: [collectionView superview]];
const CGFloat currentY = _pinchNormalisedVerticalPosition * [layout collectionViewContentSize].height;
[collectionView setContentOffset: CGPointMake(0, currentY - dragCenter.y) animated: NO];
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
[collectionView setScrollEnabled: YES];
}
default:
break;
}
}
@end