UIScrollView मोबाइल सफारी टैब की तरह क्षैतिज पेजिंग


88

मोबाइल सफारी आपको पृष्ठ पर नियंत्रण के साथ UIScrollView क्षैतिज पेजिंग दृश्य के एक प्रकार को दर्ज करके पृष्ठों को बदलने की अनुमति देता है।

मैं इस विशेष व्यवहार को दोहराने की कोशिश कर रहा हूँ जहाँ एक क्षैतिज स्क्रॉल करने योग्य UIScrollView अगले दृश्य की कुछ सामग्री दिखाता है।

Apple ने उदाहरण दिया: PageControl दिखाता है कि क्षैतिज पेजिंग के लिए UIScrollView का उपयोग कैसे किया जाता है, लेकिन सभी दृश्य पूरे स्क्रीन की चौड़ाई लेते हैं।

मुझे अगले दृश्य की कुछ सामग्री दिखाने के लिए UIScrollView कैसे मिलता है जैसे मोबाइल सफारी करता है?


2
बस एक विचार ... स्क्रॉल दृश्य की सीमा को स्क्रीन से छोटा करने का प्रयास करें, और ठीक से प्रदर्शित करने के लिए दृश्य प्राप्त करने के साथ चारों ओर फ़िश करें। (और स्क्रॉल दृश्य के क्लिप सेट करें। नहीं पर)
mjhoy

मैं यूक्रोलव्यू की चौड़ाई (क्षैतिज स्क्रॉल) से बड़े पृष्ठों को रखना चाहता था। और mjhoy के विचार ने वास्तव में मेरी मदद की!
शशो

जवाबों:


263

एक UIScrollViewसक्षम पेजिंग के साथ अपने फ्रेम चौड़ाई (या ऊंचाई) के गुणकों में बंद हो जाएगा। इसलिए पहला कदम यह पता लगाना है कि आप अपने पृष्ठों को कितना चौड़ा करना चाहते हैं। की चौड़ाई बनाओ UIScrollView। फिर, अपने सबव्यू के साइज़ को बड़े करें, लेकिन आपको उनकी ज़रूरत है, और उनके केंद्रों को UIScrollViewचौड़ाई के गुणकों के आधार पर सेट करें ।

फिर, जब से आप अन्य पृष्ठों को देखना चाहते हैं, निश्चित रूप से, जैसा कि कहा clipsToBoundsगया है NOकि mhjoy सेट करें । चाल हिस्सा अब यह स्क्रॉल करने के लिए हो रहा है जब उपयोगकर्ता खींचें की सीमा के बाहर खींचना शुरू करता है UIScrollView। मेरा समाधान (जब मुझे यह हाल ही में करना था) इस प्रकार था:

एक UIViewउपवर्ग (यानी ClipView) बनाएँ, जिसमें सम्‍मिलित होगा UIScrollViewऔर यह सबव्यू है। अनिवार्य रूप से, इसमें वह फ्रेम होना चाहिए जो आप मानेंगे कि UIScrollViewसामान्य परिस्थितियों में होगा। UIScrollViewके केंद्र में रखें ClipView। सुनिश्चित करें ClipViewकी clipsToBoundsकरने के लिए सेट कर दिया जाता YESहै, तो इसकी चौड़ाई अपनी मूल दृश्य की तुलना में कम है। इसके अलावा, ClipViewएक संदर्भ की जरूरत है UIScrollView

अंतिम चरण के - (UIView *)hitTest:withEvent:अंदर ओवरराइड करना है ClipView

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

यह मूल रूप UIScrollViewसे अपने माता-पिता के दृष्टिकोण के फ्रेम के स्पर्श क्षेत्र का विस्तार करता है , वास्तव में आपको क्या चाहिए।

एक अन्य विकल्प UIScrollViewइसकी - (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) eventविधि को उप-वर्ग और ओवरराइड करना होगा , हालांकि क्लिपिंग को करने के लिए आपको अभी भी एक कंटेनर दृश्य की आवश्यकता होगी, और यह निर्धारित करना मुश्किल हो सकता है कि YESकेवल UIScrollView'फ्रेम' के आधार पर कब लौटना है ।

नोट: आपको Juri Pakaste के हिटटेस्ट पर भी एक नज़र डालनी चाहिए : withEvent : संशोधन यदि आप सबव्यू यूजर इंटरैक्शन के साथ समस्या कर रहे हैं।


3
धन्यवाद एड। मैं UIScrollViewविचारों के आलसी लोडिंग (इन layoutSubviews) के लिए एक उपवर्ग के साथ गया , और परीक्षण clippingRectकरने के लिए उपयोग किया pointInside:withEvent:। वास्तव में अच्छी तरह से काम करता है, और कोई अतिरिक्त कंटेनर दृश्य की आवश्यकता नहीं है।
ओहोरोब

1
बहुत बढ़िया जवाब। मैं एक प्रयोग किया जाता UIScrollView@ohhorob की तरह लेकिन कतरन और वापसी के लिए एक कंटेनर को देखने के साथ उपवर्ग [super pointInside:CGPointMake(self.contentOffset.x + self.frame.size.width/2, self.contentOffset.y + self.frame.size.height/2) withEvent:event];में pointInside:withEvent:। यह बहुत अच्छी तरह से काम करता है और @ JuriPakaste के उत्तर में उल्लिखित उपयोगकर्ता सहभागिता के साथ कोई समस्या नहीं है।
23'11

1
वाह, यह वास्तव में अच्छा समाधान है। हालांकि, मेरे सेटअप पर केवल एक चीज, जो पृष्ठ मैं जोड़ रहा हूं, उसमें यूआईबटन हैं। जब मैं हिटटेस्ट विधि को ओवरराइड करता हूं तो यह मुझे उन बटन पर टैप करने की अनुमति नहीं देता है। मैं स्क्रॉल कर सकता हूं, लेकिन किसी भी पेज के अंदर कोई कार्रवाई नहीं की जा सकती।
एक सैलिसडो

8
इसके पार आने वाले लोगों के लिए हाल ही में पता चला है कि - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffsetiOS5 में UIScrollViewDelegate में जोड़ा गया है, इसका मतलब है कि आपको किसी भी अधिक इस जाल से गुजरना नहीं है।
माइक पोलार्ड

1
@MikePollard प्रभाव एक ही शांत नहीं है, targetContentOffset इस स्थिति के साथ अच्छी तरह से फिट नहीं है।
काइल फांग

70

ClipViewसमाधान ऊपर मेरे लिए काम किया है, लेकिन मैं एक अलग करना था -[UIView hitTest:withEvent:]कार्यान्वयन। एड मार्टी के वर्जन को वर्टिकल स्क्रॉलव्यूज़ के साथ काम करने वाले यूजर इंटरेक्शन नहीं मिले जो मेरे पास क्षैतिज है।

निम्नलिखित संस्करण ने मेरे लिए काम किया:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* child = nil;
    if ((child = [super hitTest:point withEvent:event]) == self)
        return self.scrollView;     
    return child;
}

यह उपयोगकर्ता सहभागिता के साथ बटन और अन्य साक्षात्कार प्राप्त करने में भी मदद करता है। :) धन्यवाद
थॉमस क्लासन

4
इस कोड के लिए धन्यवाद, मैं इस संशोधन का उपयोग कैसे कर सकता हूं कि स्क्रॉलवॉच की सीमाओं में निहित उप-बटन (बटन) से घटनाओं को प्राप्त करने के लिए? यह काम करता है अगर उदाहरण के लिए एक बटन केंद्रीय स्क्रोलव्यू की सीमाओं के भीतर है, लेकिन बाहर नहीं।
ड्रीमऑफ मिरर्स

4
इससे वाकई मदद मिली। इसे चयनित उत्तर के संशोधन के रूप में शामिल किया जाना चाहिए।
पाकु

2
हाँ! यह भी स्क्रॉलव्यू कार्य के भीतर उपयोगकर्ता का फिर से संपर्क बनाता है! मुझे काम करने के लिए इसके लिए ClipView पर YES के लिए UserInteractionEnabled को सेट करना पड़ा।
टॉम वैन ज़ुमरेन

मुझे पहले self.scrollView.subviews के माध्यम से पुनरावृत्त करना पड़ा और पहले हिटेस्ट करना पड़ा।
बाओ लेई

8

स्क्रॉल दृश्य का फ्रेम आकार निर्धारित करें क्योंकि आपके पृष्ठ का आकार होगा:

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

अब आप पैन कर सकते हैं self.view, और स्क्रॉल दृश्य पर सामग्री स्क्रॉल की जाएगी। सामग्री को क्लिप करने से रोकने के लिए
भी उपयोग scrollView.clipsToBounds = NO;करें।


5

मैं खुद को कस्टम UIScrollView के साथ जा रहा हूँ क्योंकि यह सबसे तेज और सरल तरीका था जो मुझे लगता था। हालाँकि, मुझे कोई सटीक कोड दिखाई नहीं दिया जिससे मुझे लगा कि मैं साझा करूँगा। मेरी ज़रूरतें एक UIScrollView के लिए थीं जिसमें छोटी सामग्री थी और इसलिए पेजिंग प्रभाव को प्राप्त करने के लिए UIScrollView स्वयं छोटा था। पोस्ट में कहा गया है कि आप स्वाइप नहीं कर सकते। लेकिन अब आप कर सकते हैं।

एक कस्टम CustomScrollView बनाएँ और UIScrollView को उपवर्ग बनाएँ। फिर आपको केवल इसे .m फ़ाइल में जोड़ने की आवश्यकता है:

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

यह आपको साइड से (क्षैतिज) स्क्रॉल करने की अनुमति देता है। अपने स्वाइप / स्क्रॉल टच क्षेत्र को सेट करने के लिए सीमा के अनुसार समायोजित करें। का आनंद लें!


4

मैंने एक और कार्यान्वयन किया है जो स्क्रॉलव्यू को स्वचालित रूप से वापस कर सकता है। तो यह एक IBOutlet जो परियोजना में पुन: उपयोग को सीमित करेगा की जरूरत नहीं है।

- (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;
}

1
यदि आपके पास पहले से ही एक xib / स्टोरीबोर्ड है तो इसका उपयोग करने के लिए यह एक है। अन्यथा मुझे यकीन नहीं है कि आपको पेजिंग / स्क्रॉलव्यू का संदर्भ कैसे मिलेगा। कोई विचार?
mskw

3

क्लिपव्यू हिटटेस्ट कार्यान्वयन के लिए मेरे पास एक और संभावित उपयोगी संशोधन है। मैं क्लिप व्यू को UIScrollView संदर्भ प्रदान करना पसंद नहीं करता था। नीचे मेरा कार्यान्वयन आपको किसी भी चीज़ के हिट-टेस्ट क्षेत्र का विस्तार करने के लिए क्लिपव्यू क्लास का फिर से उपयोग करने की अनुमति देता है, और इसे एक संदर्भ के साथ आपूर्ति करने की आवश्यकता नहीं है।

- (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

मैंने ऊपर दिए गए सुझाव को लागू किया, लेकिन UICollectionViewमैं स्क्रीन से दूर होने के लिए फ्रेम के बाहर कुछ भी माना जाता था। यह पास की कोशिकाओं को केवल सीमा से बाहर प्रस्तुत करने के कारण होता है जब उपयोगकर्ता उनकी ओर स्क्रॉल कर रहा था, जो आदर्श नहीं था।

मैंने जो किया, वह प्रतिनिधि (या 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;
}

यह पूरी तरह से स्वाइप एक्शन के प्रतिनिधिमंडल से बचा जाता है, जो एक बोनस भी था। UIScrollViewDelegateइसी तरह की एक विधि कहा जाता है scrollViewWillEndDragging:withVelocity:targetContentOffset:जो पेज UITableViews और UIScrollViews लिए इस्तेमाल किया जा सकता है।


2
डेव, मैं UICollectionView का उपयोग करके इस तरह के व्यवहार को प्राप्त करने के लिए संघर्ष कर रहा था और मैंने उसी दृष्टिकोण का उपयोग करके समाप्त किया, जो आप यहां वर्णित हैं। हालाँकि, स्क्रॉलिंग अनुभव उतना अच्छा नहीं है, क्योंकि यह पेजिंग सक्षम होने के साथ स्क्रॉलव्यू था। जब मैंने बड़े वेग से स्वाइप किया, तो कई पेज स्क्रॉल किए गए और यह प्रस्तावित पेज पर बंद हो गया। पेजिंग सक्षम के साथ सामान्य स्क्रॉल दृश्य के रूप में मैं व्यवहार को सहेजना चाहता हूं - मैं जो भी वेग का उपयोग करता हूं, मुझे केवल 1 पृष्ठ अधिक मिलेगा। क्या आपके पास इसका कोई उपाय है?
पीटर स्टैजर

3

इस एसओ प्रश्न की तकनीक का समर्थन करते हुए स्क्रॉल दृश्य के बच्चे के विचारों पर गोलीबारी की घटनाओं को सक्षम करें। पठनीयता के लिए स्क्रॉल दृश्य (self.scrollView) के संदर्भ का उपयोग करता है।

- (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;
}

टच इवेंट पर कब्जा करने के लिए इसे अपने बच्चे के दृश्य में जोड़ें:

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

(यह user1856273 के समाधान पर एक भिन्नता है। पठनीयता के लिए साफ किया गया और बार्टसेक के बग फिक्स को शामिल किया गया। मैंने उपयोगकर्ता के1856273 के उत्तर को संपादित करने के बारे में सोचा लेकिन यह बहुत बड़ा बदलाव था।)


सबव्यू में एम्बेडेड UIButton पर काम करने के लिए दोहन करने के लिए, मैंने कोड hitView = [childViews objectAtIndex: i] को प्रतिस्थापित किया; // के साथ (UIView * paneView के लिए UIButton को [[childViews objectAtIndex: i] सबवॉइसेस] में देखें) {if ([paneView isKindOfClass: [UIButton class]]) रिटर्न paneView; }
चार्ल्सएटा

2

मेरा संस्करण स्क्रॉल पर पड़े बटन को दबाने - काम =)

- (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;
    }

लेकिन केवल [[स्वयं के साक्षात्कार] objectAtIndex: 0] एक स्क्रॉल होना चाहिए


इससे मेरी समस्या हल हो गई :) बस ध्यान दें कि आप स्क्रॉल ऑफसेट को बिंदु से नहीं जोड़ रहे हैं। जब आप सीमा की जाँच कर रहे हैं, और इससे कोड क्षैतिज स्क्रॉल पर अच्छी तरह से काम नहीं करता है। उसे जोड़ने के बाद, यह ठीक काम किया!
बार्टसेक

2

यहां एड मार्टी के जवाब के आधार पर एक स्विफ्ट उत्तर है, लेकिन स्क्रॉल के अंदर बटन टैप आदि की अनुमति देने के लिए जूरी पकास्ट द्वारा संशोधन भी शामिल है।

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    let view = super.hitTest(point, with: event)
    return view == self ? scrollView : view
}

इसे अद्यतन करने के लिए धन्यवाद :)
लियाम बोलिंग
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.