मैं अपनी सामग्री को फिट करने के लिए UIScrollView को कैसे आकार देता हूं


130

क्या UIScrollViewस्क्रॉल करने वाली सामग्री की ऊँचाई (या चौड़ाई) पर ऑटो-समायोजन करने का कोई तरीका है ?

कुछ इस तरह:

[scrollView setContentSize:(CGSizeMake(320, content.height))];

जवाबों:


306

सबसे अच्छी विधि जो मैंने कभी भी UIScrollViewअपने समाहित साक्षात्कारों के आधार पर सामग्री के आकार को अद्यतन करने के लिए की है :

उद्देश्य सी

CGRect contentRect = CGRectZero;

for (UIView *view in self.scrollView.subviews) {
    contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;

तीव्र

let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
    rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size

9
मैं इसे viewDidLayoutSubviewsइसलिए चला रहा था ताकि ऑटोलाययट खत्म हो जाए, iOS7 में यह अच्छी तरह से काम कर रहा था, लेकिन किसी कारणवश ओएस iOS6 का परीक्षण करने से काम खत्म नहीं हुआ, इसलिए मेरे पास कुछ गलत ऊंचाई मूल्य थे, इसलिए मैंने viewDidAppearअब ठीक काम किया। बस यह इंगित करने के लिए कि शायद किसी को इसकी आवश्यकता होगी। धन्यवाद
हेटम अलीम

1
IOS6 iPad के साथ निचले दाएं कोने में रहस्यमय UIImageView (7x7) था। मैंने केवल (दृश्यमान और अल्फा) UI घटकों को स्वीकार करके इसे दूर कर दिया, फिर इसने काम किया। आश्चर्य है कि अगर उस ImageView स्क्रॉलबार से संबंधित था, निश्चित रूप से मेरा कोड नहीं।
JOM

5
स्क्रॉल संकेतक सक्षम होने पर कृपया सावधान रहें! एक UIImageView SDK द्वारा स्वचालित रूप से प्रत्येक के लिए बनाया जाएगा। आपको इसका हिसाब देना होगा अन्यथा आपकी सामग्री का आकार गलत होगा। (देखें stackoverflow.com/questions/5388703/… )
अमितप

पुष्टि कर सकते हैं कि यह समाधान स्विफ्ट 4 में मेरे लिए पूरी तरह से काम करता है
एथन हम्फ्रीज़

वास्तव में, हम CGRect.zero से यूनियन शुरू नहीं कर सकते हैं, यह केवल तभी काम करता है जब आप नकारात्मक निर्देशांक में कुछ साक्षात्कारों को घर देते हैं। आपको पहले सबव्यू से यूनियन सबव्यू फ्रेम शुरू करना चाहिए, शून्य से नहीं। उदाहरण के लिए, आपके पास केवल एक सबव्यू है जो कि फ्रेम के साथ एक UIView है (50, 50, 100, 100)। आपकी सामग्री का आकार (0, 0, 150, 150) होगा, न कि (50, 50, 100, 100)।
एवगेनी

74

UIScrollView इसकी सामग्री की ऊंचाई को स्वचालित रूप से नहीं जानता है। आपको अपने लिए ऊंचाई और चौड़ाई की गणना करनी चाहिए

इसे कुछ इस तरह से करें

CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
   scrollViewHeight += view.frame.size.height;
}

[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];

लेकिन यह केवल तभी काम करता है जब विचार एक दूसरे से नीचे हों। यदि आपके पास एक दूसरे के बगल में एक दृश्य है, तो आपको केवल एक की ऊंचाई को जोड़ना होगा यदि आप नहीं चाहते कि स्कोरर की सामग्री वास्तव में इससे बड़ी हो।


क्या आपको लगता है कि ऐसा कुछ काम करेगा? CGFloat ScrollViewHeight = scrollView.contentSize.height; [स्क्रॉल दृश्य सेट करें: (CGSizeMake (320, स्क्रॉल दृश्य))]; BTW, आकार तो ऊंचाई के लिए पूछना मत भूलना। स्क्रॉलव्यूहाइट + = view.frame.size.height
jwerre

स्क्रॉलव्यूहाइट को ऊँचाई तक ले जाने से स्कोरर अपनी सामग्री से बड़ा हो जाता है। यदि आप ऐसा करते हैं तो स्कोरर सामग्री की प्रारंभिक ऊँचाई पर दिखाई देने वाले दृश्यों की ऊँचाई को नहीं जोड़ें। हालांकि हो सकता है कि आपको शुरुआती अंतराल या कुछ और चाहिए।
एमेनग्रो

21
या बस करो:int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
गैल

@ संताज, धन्यवाद, मैंने इसे औपचारिक उत्तर के रूप में जोड़ा :) :)
Gal

40

समाधान यदि आप ऑटो लेआउट का उपयोग कर रहे हैं:

  • सेट translatesAutoresizingMaskIntoConstraintsकरने के लिए NOशामिल सभी दृश्यों पर।

  • स्थिति और बाहरी स्क्रॉल के साथ अपने स्क्रॉल दृश्य को स्क्रॉल आकार में देखें।

  • स्क्रॉल दृश्य के भीतर उपविभाजन बिछाने के लिए अवरोधों का उपयोग करें, यह सुनिश्चित करते हुए कि स्क्रॉल स्क्रॉल के चारों किनारों पर बाधाएं बंधी हैं और अपने आकार को प्राप्त करने के लिए स्क्रॉल दृश्य पर निर्भर नहीं हैं।

स्रोत: https://developer.apple.com/library/ios/technotes/tn2154/_index.html


10
Imho यह दुनिया में वास्तविक समाधान है जहां सब कुछ ऑटो लेआउट के साथ होता है। अन्य समाधानों के बारे में: क्या ... आप हर दृश्य की ऊँचाई और कभी-कभी चौड़ाई की गणना करने में गंभीर हैं?
फॉक्स

इसे साझा करने के लिए आपको धन्यवाद! यही स्वीकृत उत्तर होना चाहिए।
सेप्रोब्लाम

2
जब आप स्क्रॉलव्यू की ऊंचाई को उसकी सामग्री की ऊँचाई के आधार पर रखना चाहते हैं तो यह काम नहीं करता है। (उदाहरण के लिए एक पॉपअप स्क्रीन में केंद्रित है जहाँ आप सामग्री की एक निश्चित मात्रा तक स्क्रॉल नहीं करना चाहते हैं)।
LeGom

यह इस सवाल का जवाब नहीं देता है
इगोर

महान समाधान। हालांकि, मैं एक और गोली जोड़ूंगा ... "यदि यह लंबवत रूप से बढ़ना चाहिए तो बच्चे के स्टैक दृश्य की ऊंचाई को सीमित न करें। इसे स्क्रॉल दृश्य के किनारों पर पिन करें।"
एंड्रयू किर्ना

35

मैंने एस्पुज और जेसीसी के जवाब में इसे जोड़ा। यह सबव्यू की y स्थिति का उपयोग करता है और इसमें स्क्रॉल बार शामिल नहीं हैं। एडिट सबसे कम उप दृश्य के निचले भाग का उपयोग करता है जो दिखाई देता है।

+ (CGFloat) bottomOfLowestContent:(UIView*) view
{
    CGFloat lowestPoint = 0.0;

    BOOL restoreHorizontal = NO;
    BOOL restoreVertical = NO;

    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if ([(UIScrollView*)view showsHorizontalScrollIndicator])
        {
            restoreHorizontal = YES;
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:NO];
        }
        if ([(UIScrollView*)view showsVerticalScrollIndicator])
        {
            restoreVertical = YES;
            [(UIScrollView*)view setShowsVerticalScrollIndicator:NO];
        }
    }
    for (UIView *subView in view.subviews)
    {
        if (!subView.hidden)
        {
            CGFloat maxY = CGRectGetMaxY(subView.frame);
            if (maxY > lowestPoint)
            {
                lowestPoint = maxY;
            }
        }
    }
    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if (restoreHorizontal)
        {
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:YES];
        }
        if (restoreVertical)
        {
            [(UIScrollView*)view setShowsVerticalScrollIndicator:YES];
        }
    }

    return lowestPoint;
}

1
आश्चर्यजनक! मुझे जिस चीज की जरूरत थी।
रिचर्ड

2
मैं इस तरह से एक वर्ग के भाग नहीं एक विधि आश्चर्यचकित हूँ।
जोआ पोटेला

तुमने ऐसा क्यों किया self.scrollView.showsHorizontalScrollIndicator = NO; self.scrollView.showsVerticalScrollIndicator = NO; शुरुआत में और self.scrollView.showsHorizontalScrollIndicator = YES; self.scrollView.showsVerticalScrollIndicator = YES;विधि के अंत में ?
जोआ पोटेला

धन्यवाद समृद्ध :) उत्तर के लिए +1।
श्राद्ध

1
यदि आप सही स्क्रॉल संकेतक हैं तो दृश्यमान हैं, लेकिन लॉजिक को छिपाना / दिखाना गलत है। आपको दो BOOL लोकल जोड़ने की आवश्यकता है: RestoreHor क्षैतिज = NO, RestoreVertical = NO। यदि शो हॉरिज़ॉन्टल, इसे छिपाएँ, रिस्टोरहर्ल को यस में सेट करें। वर्टिकल के लिए भी। अगला, के बाद / के लिए लूप इन्सर्ट में यदि स्टेटमेंट: यदि रिस्टोरहर्बल, इसे दिखाने के लिए सेट है, तो वर्टिकल के लिए भी। आपका कोड बस दोनों संकेतकों को दिखाने के लिए मजबूर करेगा
अवैध-आप्रवासी

13

यहाँ किसी को भी जो इसे बदलने के लिए बहुत आलसी है स्विफ्ट में स्वीकृत उत्तर दिया गया है :)

var contentRect = CGRectZero
for view in self.scrollView.subviews {
    contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size

self.scrollView.subviews में दृश्य {contentRect = contentRect.union (view.frame)} self.scrollView.contentSize = contentRect.size के लिए वर contentRect = CGRect.zero: और Swift3 के लिए अद्यतन
आकर्षित किया ..

क्या इसे मुख्य धागे पर बुलाया जाना है? और didLayoutSubViews में?
मक्सिम नियाज़ेव

8

यहां @ लेविटैन के उत्तर का एक स्विफ्ट 3 अनुकूलन है:

विस्तार

import UIKit


extension UIScrollView {

    func resizeScrollViewContentSize() {

        var contentRect = CGRect.zero

        for view in self.subviews {

            contentRect = contentRect.union(view.frame)

        }

        self.contentSize = contentRect.size

    }

}

उपयोग

scrollView.resizeScrollViewContentSize()

उपयोग करने के लिए बहुत आसान है!


बहुत उपयोगी। इतने सारे पुनरावृत्ति के बाद मुझे यह समाधान मिला और यह एकदम सही है।
हार्दिक शाह

लवली! बस इसे लागू किया।
आर्विडर्स

7

निम्नलिखित विस्तार स्विफ्ट में सहायक होगा ।

extension UIScrollView{
    func setContentViewSize(offset:CGFloat = 0.0) {
        // dont show scroll indicators
        showsHorizontalScrollIndicator = false
        showsVerticalScrollIndicator = false

        var maxHeight : CGFloat = 0
        for view in subviews {
            if view.isHidden {
                continue
            }
            let newHeight = view.frame.origin.y + view.frame.height
            if newHeight > maxHeight {
                maxHeight = newHeight
            }
        }
        // set content size
        contentSize = CGSize(width: contentSize.width, height: maxHeight + offset)
        // show scroll indicators
        showsHorizontalScrollIndicator = true
        showsVerticalScrollIndicator = true
    }
}

तर्क दिए गए उत्तरों के साथ समान है। हालाँकि, यह भीतर छिपे हुए विचारों को छोड़ देता है UIScrollViewऔर गणना को स्क्रॉल संकेतकों को छिपाए जाने के बाद किया जाता है।

इसके अलावा, एक वैकल्पिक फ़ंक्शन पैरामीटर है और आप कार्य करने के लिए पैरामीटर पास करके ऑफ़सेट मान जोड़ सकते हैं।


इस समाधान में बहुत अधिक धारणाएं हैं, आप स्क्रॉल संकेतक को एक विधि में क्यों दिखा रहे हैं जो सामग्री का आकार निर्धारित करता है?
कप्पे

5

@Leviathan से महान और सबसे अच्छा समाधान। एफपी (कार्यात्मक प्रोग्रामिंग) दृष्टिकोण का उपयोग कर तेजी से अनुवाद करने के लिए।

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), { 
  CGRectUnion($0, $1.frame) 
}.size

यदि आप छिपे हुए विचारों को अनदेखा करना चाहते हैं तो आप कम करने के लिए गुजरने से पहले साक्षात्कार फ़िल्टर कर सकते हैं।
एंडी

स्विफ्ट 3 के लिए अपडेट किया गया:self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
जॉन रोजर्स

4

आप UIScrollView के अंदर सामग्री की ऊंचाई की गणना कर सकते हैं कि कौन सा बच्चा "पंखों तक पहुंचता है"। यह गणना करने के लिए आपको मूल Y (प्रारंभ) और आइटम की ऊँचाई को ध्यान में रखना होगा।

float maxHeight = 0;
for (UIView *child in scrollView.subviews) {
    float childHeight = child.frame.origin.y + child.frame.size.height;
    //if child spans more than current maxHeight then make it a new maxHeight
    if (childHeight > maxHeight)
        maxHeight = childHeight;
}
//set content size
[scrollView setContentSize:(CGSizeMake(320, maxHeight))];

इस तरह से चीजें करने से आइटम (साक्षात्कार) को सीधे एक दूसरे के नीचे स्टैक नहीं करना पड़ता है।


आप इस एक लाइनर को लूप के अंदर भी उपयोग कर सकते हैं: मैक्सहाइट = मैक्स (मैक्सहाइट, चाइल्ड.फ्रेम ।origin.y + child.frame.size.height) ;)
थॉमस सीजी डी विलेना

3

मैं @ emenegro के समाधान के आधार पर एक और समाधान के साथ आया था

NSInteger maxY = 0;
for (UIView* subview in scrollView.subviews)
{
    if (CGRectGetMaxY(subview.frame) > maxY)
    {
        maxY = CGRectGetMaxY(subview.frame);
    }
}
maxY += 10;
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, maxY)];

असल में, हम यह पता लगाते हैं कि कौन सा तत्व दृश्य में सबसे नीचे है और नीचे की ओर 10px पैडिंग जोड़ता है


3

क्योंकि एक स्क्रॉल दृश्य में अन्य स्क्रॉल दृश्य या भिन्न inDepth उप दृश्य पेड़ हो सकते हैं, गहराई से पुनरावर्ती रूप से चलाना बेहतर होता है। यहाँ छवि विवरण दर्ज करें

स्विफ्ट 2

extension UIScrollView {
    //it will block the mainThread
    func recalculateVerticalContentSize_synchronous () {
        let unionCalculatedTotalRect = recursiveUnionInDepthFor(self)
        self.contentSize = CGRectMake(0, 0, self.frame.width, unionCalculatedTotalRect.height).size;
    }

    private func recursiveUnionInDepthFor (view: UIView) -> CGRect {
        var totalRect = CGRectZero
        //calculate recursevly for every subView
        for subView in view.subviews {
            totalRect =  CGRectUnion(totalRect, recursiveUnionInDepthFor(subView))
        }
        //return the totalCalculated for all in depth subViews.
        return CGRectUnion(totalRect, view.frame)
    }
}

प्रयोग

scrollView.recalculateVerticalContentSize_synchronous()

2

या बस करो:

int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];

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


2

कम करने का उपयोग कर Swift4 के लिए:

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
   return $0.union($1.frame)
}).size

यह भी जांचने योग्य है कि क्या कम से कम एक सबव्यू के मूल == CGPoint.zero है या बस एक विशेष (उदाहरण के लिए सबसे बड़ा) सबवू की उत्पत्ति को शून्य पर असाइन करें।
पॉल बी

यह केवल उन लोगों के लिए काम करता है जो CGRect () के साथ अपने विचार रखते हैं। यदि आप बाधाओं का उपयोग कर रहे हैं तो यह काम नहीं कर रहा है।
linus_hologram

1

आकार उसके अंदर भरी हुई सामग्री और कतरन विकल्पों पर निर्भर करता है। यदि इसका एक टेक्स्टव्यू है, तो यह रैपिंग पर भी निर्भर करता है कि टेक्स्ट की कितनी लाइनें, फॉन्ट साइज, और इसी तरह और कई चीजें। आपके लिए खुद की गणना करना लगभग असंभव है। अच्छी खबर यह है, यह देखने और लोड करने के बाद गणना की जाती है। इससे पहले, यह सभी अज्ञात है और सामग्री आकार फ्रेम आकार के समान होगा। लेकिन, viewWillAppear मेथड में और उसके बाद (जैसे viewDidAppear) कंटेंट साइज वास्तविक होगा।


1

रिची के कोड को रैप करते हुए मैंने एक कस्टम UIScrollView वर्ग बनाया जो पूरी तरह से आकार देने वाली सामग्री को स्वचालित करता है!

SBScrollView.h

@interface SBScrollView : UIScrollView
@end

SBScrollView.m:

@implementation SBScrollView
- (void) layoutSubviews
{
    CGFloat scrollViewHeight = 0.0f;
    self.showsHorizontalScrollIndicator = NO;
    self.showsVerticalScrollIndicator = NO;
    for (UIView* view in self.subviews)
    {
        if (!view.hidden)
        {
            CGFloat y = view.frame.origin.y;
            CGFloat h = view.frame.size.height;
            if (y + h > scrollViewHeight)
            {
                scrollViewHeight = h + y;
            }
        }
    }
    self.showsHorizontalScrollIndicator = YES;
    self.showsVerticalScrollIndicator = YES;
    [self setContentSize:(CGSizeMake(self.frame.size.width, scrollViewHeight))];
}
@end

कैसे उपयोग करें:
बस अपने। नियंत्रक के लिए .h फ़ाइल आयात करें और सामान्य UIScrollView एक के बजाय एक SBScrollView उदाहरण घोषित करें।


2
लेआउटसुब्यू ऐसा लगता है कि यह ऐसे लेआउट से संबंधित कोड के लिए सही जगह है। लेकिन UIScrollView के लेआउट में, जब भी स्क्रॉल किया जाता है, तो हर बार कंटेंट ऑफ़सेट अपडेट हो जाता है। और चूंकि सामग्री का आकार आमतौर पर नहीं बदलता है, इसलिए स्क्रॉल करते समय यह बेकार है।
स्वेन

यह सच है। इस अक्षमता से बचने का एक तरीका यह भी हो सकता है कि [self isDragging] और [self isDecelerating] की जाँच करें और केवल लेआउट का प्रदर्शन करें यदि दोनों झूठे हैं।
ग्रेग ब्राउन

1

डायनामिक कंटेंट का आकार इस तरह सेट करें।

 self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);

0

यह वास्तव में सामग्री पर निर्भर करता है: content.frame.height आपको वह दे सकता है जो आप चाहते हैं? निर्भर करता है कि क्या सामग्री एक चीज है, या चीजों का संग्रह है।


0

मुझे सबसे अच्छा काम करने के लिए लेविथान का जवाब भी मिला। हालांकि, यह एक अजीब ऊंचाई की गणना कर रहा था। जब सबव्यू के माध्यम से लूपिंग किया जाता है, अगर स्क्रॉल संकेतक स्क्रॉल संकेतक दिखाने के लिए सेट किया जाता है, तो वे सबव्यू की सरणी में होंगे। इस मामले में, समाधान लूपिंग से पहले स्क्रॉल संकेतक को अस्थायी रूप से अक्षम करना है, फिर उनकी पिछली दृश्यता सेटिंग को फिर से स्थापित करें।

-(void)adjustContentSizeToFit UIScrollView के कस्टम उपवर्ग पर एक सार्वजनिक विधि है।

-(void)awakeFromNib {    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self adjustContentSizeToFit];
    });
}

-(void)adjustContentSizeToFit {

    BOOL showsVerticalScrollIndicator = self.showsVerticalScrollIndicator;
    BOOL showsHorizontalScrollIndicator = self.showsHorizontalScrollIndicator;

    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;

    CGRect contentRect = CGRectZero;
    for (UIView *view in self.subviews) {
        contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.contentSize = contentRect.size;

    self.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
    self.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
}

0

मुझे लगता है कि यह UIScrollView के सामग्री दृश्य आकार को अपडेट करने का एक अच्छा तरीका हो सकता है।

extension UIScrollView {
    func updateContentViewSize() {
        var newHeight: CGFloat = 0
        for view in subviews {
            let ref = view.frame.origin.y + view.frame.height
            if ref > newHeight {
                newHeight = ref
            }
        }
        let oldSize = contentSize
        let newSize = CGSize(width: oldSize.width, height: newHeight + 20)
        contentSize = newSize
    }
}

0

कोड की एक पंक्ति क्यों नहीं ??

_yourScrollView.contentSize = CGSizeMake(0, _lastView.frame.origin.y + _lastView.frame.size.height);

0
import UIKit

class DynamicSizeScrollView: UIScrollView {

    var maxHeight: CGFloat = UIScreen.main.bounds.size.height
    var maxWidth: CGFloat = UIScreen.main.bounds.size.width

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size,self.intrinsicContentSize){
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        let height = min(contentSize.height, maxHeight)
        let width = min(contentSize.height, maxWidth)
        return CGSize(width: width, height: height)
    }

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