Uiscrollview poziome stronicowanie jak Mobilne Karty Safari

Mobile Safari Umożliwia przełączanie stron poprzez wprowadzenie pewnego rodzaju poziomego widoku stronicowania uiscrollview z kontrolką strony u dołu.

Próbuję odtworzyć to zachowanie, w którym przewijany poziomo UIScrollView pokazuje część zawartości następnego widoku.

Przykład Apple: PageControl pokazuje, jak używać widoku uiscrollview do stronicowania poziomego, ale wszystkie widoki zajmują całą szerokość ekranu.

Jak uzyskać UIScrollView, aby pokazać niektóre zawartość następnego widoku jak w mobilnym Safari?

Author: Corey Floyd, 2009-08-03

9 answers

A {[1] } z włączonym stronicowaniem zatrzyma się przy wielokrotnościach szerokości ramki (lub wysokości). Pierwszym krokiem jest ustalenie, jak szeroka ma być Twoja strona. Niech będzie to szerokość UIScrollView. Następnie Ustaw rozmiary subview, niezależnie od tego, jakie są duże, i ustaw ich centra na podstawie wielokrotności szerokości UIScrollView.

Następnie, ponieważ chcesz zobaczyć inne strony, oczywiście ustaw clipsToBounds na NO, jak stwierdził mhjoy. Sztuczka polega teraz na tym, aby przewijać go, gdy użytkownik zacznie przeciągnięcie poza zakres ramki UIScrollView. Moje rozwiązanie (kiedy musiałem to zrobić bardzo niedawno) było następujące: {]}

Utwórz podklasę UIView (tj. ClipView), która będzie zawierać UIScrollView i jest to podwidywalność. Zasadniczo powinien mieć ramę tego, co można by założyć, że UIScrollView będzie miał w normalnych okolicznościach. Umieść UIScrollView w środku ClipView. Upewnij się, że ClipView ' S clipsToBounds jest ustawiona na YES, jeśli jej szerokość jest mniejsza niż szerokość widoku nadrzędnego. Ponadto ClipView potrzebuje odniesienie do UIScrollView.

Ostatnim krokiem jest obejście - (UIView *)hitTest:withEvent: wewnątrz ClipView.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  return [self pointInside:point withEvent:event] ? scrollView : nil;
}

To zasadniczo rozszerza obszar dotykowy UIScrollView do ramki widoku rodzica, dokładnie tego, czego potrzebujesz.

Inną opcją byłoby podklasowanie UIScrollView i nadpisanie jej metody - (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event, jednak nadal będziesz potrzebował widoku kontenera do przycinania i może być trudno określić, kiedy zwrócić YES tylko na podstawie ramki UIScrollView.

Uwaga: powinieneś również spojrzeć na Juri Pakaste hitTest: withEvent: modification jeśli masz problemy z interakcją z subview.

 260
Author: Ed Marty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:16:51

Powyższe rozwiązanie zadziałało dla mnie, ale musiałem wykonać inną implementację -[UIView hitTest:withEvent:]. Wersja Ed Marty ' ego nie dostała interakcji z użytkownikiem pracującym z pionowymi przewijaniami, które mam wewnątrz poziomego.

Następująca wersja zadziałała dla mnie:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* child = nil;
    if ((child = [super hitTest:point withEvent:event]) == self)
        return self.scrollView;     
    return child;
}
 69
Author: Juri Pakaste,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-03 12:25:46

Ustaw rozmiar ramki scrollView jako rozmiar stron:

[self.view addSubview:scrollView];
[self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];

Teraz możesz przesuwać self.view, a zawartość w widoku przewijania zostanie przewinięta.
Użyj również scrollView.clipsToBounds = NO;, aby zapobiec przycinaniu zawartości.

 8
Author: Nikolay Tabunchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-27 22:32:44

Sam zacząłem korzystać z niestandardowego UIScrollView, ponieważ była to najszybsza i prostsza metoda, jaką mi się wydawało. Jednak nie widziałem żadnego dokładnego kodu, więc pomyślałem, że podzielę się. Moje potrzeby były dla UIScrollView, który miał małą zawartość i dlatego sam UIScrollView był mały, aby osiągnąć wpływ stronicowania. Jak pisze post, nie możesz przesuwać palcem. Ale teraz możesz.

Utwórz klasę CustomScrollView i podklasę UIScrollView. Następnie wszystko, co musisz zrobić, to dodać to do.na plik:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
  return (point.y >= 0 && point.y <= self.frame.size.height);
}

To pozwala na przewijanie z boku na bok (poziomo). Dostosuj odpowiednio granice, aby ustawić obszar dotykowy przesuwania / przewijania. Smacznego!

 5
Author: djneely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-15 16:29:06

Wykonałem kolejną implementację, która może automatycznie zwracać widok przewijania. Więc nie musi mieć IBOutlet, który ograniczy ponowne użycie w projekcie.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ([self pointInside:point withEvent:event]) {
        for (id view in [self subviews]) {
            if ([view isKindOfClass:[UIScrollView class]]) {
                return view;
            }
        }
    }
    return nil;
}
 4
Author: alvinhu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-17 10:28:59

Mam kolejną potencjalnie przydatną modyfikację dla implementacji clipview hitTest. Nie podobało mi się, że trzeba podać odniesienie UIScrollView do ClipView. Moja implementacja poniżej pozwala na ponowne użycie klasy ClipView, aby rozszerzyć obszar hit-test czegokolwiek, i nie trzeba dostarczać mu odniesienia.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (([self pointInside:point withEvent:event]) && (self.subviews.count >= 1))
    {
        // An extended-hit view should only have one sub-view, or make sure the
        // first subview is the one you want to expand the hit test for.
        return [self.subviews objectAtIndex:0];
    }

    return nil;
}
 3
Author: Rod Reddekopp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-09 07:52:37

Zaimplementowałem powyższą sugestię, ale UICollectionView używałem czegokolwiek poza kadrem, aby być poza ekranem. Spowodowało to, że pobliskie komórki renderowały poza granicami tylko wtedy, gdy użytkownik przewijał w ich kierunku, co nie było idealne.

Skończyło się na emulowaniu zachowania widoku przewijania przez dodanie poniższej metody do delegata (lub UICollectionViewLayout).

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{    
  if (velocity.x > 0) {
    proposedContentOffset.x = ceilf(self.collectionView.contentOffset.x / pageSize) * pageSize;
  }
  else {
    proposedContentOffset.x = floorf(self.collectionView.contentOffset.x / pageSize) * pageSize;
  }

  return proposedContentOffset;
}

Pozwala to całkowicie uniknąć delegowania akcji machnięcia, co było również bonusem. UIScrollViewDelegate ma podobna metoda o nazwie scrollViewWillEndDragging:withVelocity:targetContentOffset:, która może być użyta do stronicowania UITableViews i uiscrollviews.

 3
Author: Kyle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-30 00:17:39

Włączanie zdarzeń stukania w dziecięcych widokach widoku przewijania, wspierając technikę tego pytania. Używa odniesienia do widoku przewijania (self.scrollView) dla czytelności.

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = nil;
    NSArray *childViews = [self.scrollView subviews];
    for (NSUInteger i = 0; i < childViews.count; i++) {
        CGRect childFrame = [[childViews objectAtIndex:i] frame];
        CGRect scrollFrame = self.scrollView.frame;
        CGPoint contentOffset = self.scrollView.contentOffset;
        if (childFrame.origin.x + scrollFrame.origin.x < point.x + contentOffset.x &&
            point.x + contentOffset.x < childFrame.origin.x + scrollFrame.origin.x + childFrame.size.width &&
            childFrame.origin.y + scrollFrame.origin.y < point.y + contentOffset.y &&
            point.y + contentOffset.y < childFrame.origin.y + scrollFrame.origin.y + childFrame.size.height
        ){
            hitView = [childViews objectAtIndex:i];
            return hitView;
        }
    }
    hitView = [super hitTest:point withEvent:event];
    if (hitView == self)
        return self.scrollView;
    return hitView;
}

Dodaj to do widoku dziecka, aby uchwycić Zdarzenie dotykowe:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

(jest to wariant rozwiązania user1856273. Oczyszczone pod kątem czytelności i włączone poprawki błędów Bartserka. Myślałem o edytowaniu odpowiedzi user1856273, ale to była zbyt duża zmiana, aby dokonać.)

 3
Author: David James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-25 17:49:49

Moja wersja Naciśnięcie przycisku leżącego na przewijaku-work =)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* child = nil;
    for (int i=0; i<[[[[self subviews] objectAtIndex:0] subviews] count];i++) {

        CGRect myframe =[[[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i] frame];
        CGRect myframeS =[[[self subviews] objectAtIndex:0] frame];
        CGPoint myscroll =[[[self subviews] objectAtIndex:0] contentOffset];
        if (myframe.origin.x < point.x && point.x < myframe.origin.x+myframe.size.width &&
            myframe.origin.y+myframeS.origin.y < point.y+myscroll.y && point.y+myscroll.y < myframe.origin.y+myframeS.origin.y +myframe.size.height){
            child = [[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i];
            return child;
        }


    }

    child = [super hitTest:point withEvent:event];
    if (child == self)
        return [[self subviews] objectAtIndex:0];
    return child;
    }

Ale tylko [[self subviews] objectAtIndex: 0] musi być zwój

 2
Author: kolbasek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-15 16:57:07