【问题标题】:UIScrollView with dynamic Footer带有动态页脚的 UIScrollView
【发布时间】:2021-03-18 21:41:10
【问题描述】:

我在 ScrollView 中有一个 Label 和一个 ContainerView(带有一个 Label 和 Switch 作为子项)。 我希望我的 ContainerView 在标签底部有一个填充,但是如果标签 高度太大(需要滚动),页脚应该贴在屏幕底部(不应该滚动)。是否可以使用 AutoLayout 来做到这一点?

【问题讨论】:

  • 您是在 Storyboard 中进行布局吗?还是通过代码?
  • 我想用故事板布局,但我不知道它是否可以用自动布局

标签: ios user-interface autolayout storyboard scrollview


【解决方案1】:

有几种方法可以做到这一点...

我建议您将“切换容器视图”置于滚动视图之外,固定在滚动视图的底部。当滚动视图的内容(多行标签)改变高度时,我们将改变滚动视图的高度——直到开关容器到达视图的底部(安全-区域底部)。

它在 Storyboard 中的外观如下:

多行标签背景为青色,开关容器视图背景为黄色。

请注意,我们已将滚动视图的高度限制为多行标签的高度,但将优先级设置为750 (High)。通过将其从Required更改为High,我们告诉自动布局在需要时打破该约束。也就是说,当它对于可用空间来说太高时。

还要注意,开关容器视图的底部约束设置为>= 0。当我们将文本添加到多行标签时,它会导致滚动视图的高度增加将容器视图向下推 ...但直到它到达视图的底部(安全区域)。

我们在这里看不到的唯一一件事 - 但它很重要 - 是我们将多行标签和 Switch 的 Vertical Content Compression Resistance 设置为 1000 (Required)

这是该故事板的来源,您可以查看它:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="XCt-Hc-wQm">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Scroll Footer View Controller-->
        <scene sceneID="Caf-Eb-sIu">
            <objects>
                <viewController id="XCt-Hc-wQm" customClass="ScrollFooterViewController" customModule="DelMe" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="dUm-Xz-ZMW">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="USX-Ck-yG1">
                                <rect key="frame" x="0.0" y="0.0" width="375" height="21.5"/>
                                <subviews>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lwg-yg-39D">
                                        <rect key="frame" x="0.0" y="0.0" width="375" height="21.5"/>
                                        <color key="backgroundColor" red="0.45138680930000002" green="0.99309605359999997" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <fontDescription key="fontDescription" type="system" pointSize="18"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                </subviews>
                                <color key="backgroundColor" red="1" green="0.14913141730000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="height" secondItem="lwg-yg-39D" secondAttribute="height" priority="750" id="76J-30-uTn"/>
                                    <constraint firstItem="lwg-yg-39D" firstAttribute="trailing" secondItem="ZIz-K9-t2b" secondAttribute="trailing" id="GCE-Kl-AnT"/>
                                    <constraint firstItem="lwg-yg-39D" firstAttribute="leading" secondItem="ZIz-K9-t2b" secondAttribute="leading" id="KKY-k0-5P6"/>
                                    <constraint firstItem="lwg-yg-39D" firstAttribute="top" secondItem="ZIz-K9-t2b" secondAttribute="top" id="MY4-LC-VHJ"/>
                                    <constraint firstItem="lwg-yg-39D" firstAttribute="bottom" secondItem="ZIz-K9-t2b" secondAttribute="bottom" id="dnk-39-RlU"/>
                                    <constraint firstItem="lwg-yg-39D" firstAttribute="width" secondItem="PfO-IK-xkv" secondAttribute="width" id="zrQ-RH-rly"/>
                                </constraints>
                                <viewLayoutGuide key="contentLayoutGuide" id="ZIz-K9-t2b"/>
                                <viewLayoutGuide key="frameLayoutGuide" id="PfO-IK-xkv"/>
                            </scrollView>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Njo-Q9-KiY" userLabel="ContainerView">
                                <rect key="frame" x="0.0" y="21.5" width="375" height="47"/>
                                <subviews>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vUU-Ps-Crk" userLabel="SwitchLabel">
                                        <rect key="frame" x="8" y="13" width="42" height="21"/>
                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="24L-2e-H7p">
                                        <rect key="frame" x="318" y="8" width="51" height="31"/>
                                    </switch>
                                </subviews>
                                <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstItem="vUU-Ps-Crk" firstAttribute="centerY" secondItem="24L-2e-H7p" secondAttribute="centerY" id="Dwk-0c-XGq"/>
                                    <constraint firstAttribute="trailing" secondItem="24L-2e-H7p" secondAttribute="trailing" constant="8" id="Vuj-Vf-Ubm"/>
                                    <constraint firstItem="vUU-Ps-Crk" firstAttribute="leading" secondItem="Njo-Q9-KiY" secondAttribute="leading" constant="8" id="VzC-He-6eA"/>
                                    <constraint firstItem="24L-2e-H7p" firstAttribute="top" secondItem="Njo-Q9-KiY" secondAttribute="top" constant="8" id="xVS-9o-J0n"/>
                                    <constraint firstAttribute="bottom" secondItem="24L-2e-H7p" secondAttribute="bottom" constant="8" id="yR9-XP-FQq"/>
                                </constraints>
                            </view>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="y57-Pg-ijO"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <constraints>
                            <constraint firstItem="y57-Pg-ijO" firstAttribute="trailing" secondItem="Njo-Q9-KiY" secondAttribute="trailing" id="7PH-bB-Xu1"/>
                            <constraint firstItem="USX-Ck-yG1" firstAttribute="top" secondItem="y57-Pg-ijO" secondAttribute="top" id="RRh-cW-cdn"/>
                            <constraint firstItem="y57-Pg-ijO" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Njo-Q9-KiY" secondAttribute="bottom" id="ZhG-3Y-WiX"/>
                            <constraint firstItem="Njo-Q9-KiY" firstAttribute="leading" secondItem="y57-Pg-ijO" secondAttribute="leading" id="aj2-fS-rab"/>
                            <constraint firstItem="Njo-Q9-KiY" firstAttribute="top" secondItem="USX-Ck-yG1" secondAttribute="bottom" id="eeo-Zi-yar"/>
                            <constraint firstItem="USX-Ck-yG1" firstAttribute="trailing" secondItem="y57-Pg-ijO" secondAttribute="trailing" id="iK0-ax-YU6"/>
                            <constraint firstItem="USX-Ck-yG1" firstAttribute="leading" secondItem="y57-Pg-ijO" secondAttribute="leading" id="lMU-A2-QsG"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="multiLineLabel" destination="lwg-yg-39D" id="huD-0B-amT"/>
                        <outlet property="scrollView" destination="USX-Ck-yG1" id="2c5-7h-7Ii"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="cjI-ra-ksU" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1586.4000000000001" y="266.71664167916043"/>
        </scene>
    </scenes>
    <resources>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
    </resources>
</document>

以及一些示例代码来演示:

class ScrollFooterViewController: UIViewController {
    
    @IBOutlet var scrollView: UIScrollView!
    @IBOutlet var multiLineLabel: UILabel!
    
    var numLines: Int = 0
    var addLine: Bool = true
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // add a tap recognizer...
        //  each time we tap the view, we'll add text from the sample text array
        //  until we reach the end of the array
        //  then, each tap will remove text until we reach 1
        let t = UITapGestureRecognizer(target: self, action: #selector(self.updateMultilineLabel))
        view.addGestureRecognizer(t)
        
        // init the multiline label text
        updateMultilineLabel()
    }
    
    @objc func updateMultilineLabel() -> Void {
        // are we adding or removing text?
        if addLine == true {
            numLines += 1
        } else {
            numLines -= 1
            // keep at least 1 line of text
            numLines = max(numLines, 1)
        }
        
        // generate string with numLines of text from sample text array
        let s = sampleText[0..<numLines].joined(separator: "\n\n")
        
        // set text of multiline label
        multiLineLabel.text = s
        
        // auto-scroll to the bottom
        scrollToBottom(scrollView)
        
        // if we're at the end of the sample text array, switch from adding to removing
        if numLines == sampleText.count {
            addLine = false
        }
        // if we're at 1, switch from removing to adding
        if numLines == 1 {
            addLine = true
        }
    }
    
    func scrollToBottom(_ scrollView: UIScrollView) -> Void {
        // set content offset after 0.01 seconds (to make sure it runs after the label has updated its frame)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
            let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
            if(bottomOffset.y > 0) {
                scrollView.setContentOffset(bottomOffset, animated: true)
            }
        })
    }
    
    let sampleText: [String] = [
        "UILabel\n\nA label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label.",
        "UIButton\n\nYou can set the title, image, and other appearance properties of a button. In addition, you can specify a different appearance for each button state.",
        "UISegmentedControl\n\nThe segments can represent single or multiple selection, or a list of commands.\n\nEach segment can display text or an image, but not both.",
        "UITextField\n\nDisplays a rounded rectangle that can contain editable text. When a user taps a text field, a keyboard appears; when a user taps Return in the keyboard, the keyboard disappears and the text field can handle the input in an application-specific way. UITextField supports overlay views to display additional information, such as a bookmarks icon. UITextField also provides a clear text control a user taps to erase the contents of the text field.",
        "UISlider\n\nUISlider displays a horizontal bar, called a track, that represents a range of values. The current value is shown by the position of an indicator, or thumb. A user selects a value by sliding the thumb along the track. You can customize the appearance of both the track and the thumb.",
        "UISwitch\n\nDisplays an element that shows the user the boolean state of a given value.  By tapping the control, the state can be toggled.",
    ]
    
}

代码有一个“示例文本”数组...每次点击屏幕时,多行标签会添加更多文本,直到我们到达数组末尾,此时每次点击都会删除一些文字。

结果如下:

还有一点文字,但还不足以需要滚动:

并且有足够的文本,我们需要滚动:

【讨论】:

    【解决方案2】:

    我在之前的项目中做过,这里是代码

    @IBOutlet weak var scrollViewHeightConstraints: NSLayoutConstraint!
    override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
            let labelHeight = self.textLabel.frame.size.height + 60
            let screenHeight = UIScreen.main.bounds.size.height - 100
            self.scrollViewHeightConstraints.constant = min(screenHeight, labelHeight)
        }
    

    常量 60 和 100 可以根据您的视图进行更改。

    【讨论】:

      猜你喜欢
      • 2014-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多