Search code examples
iosswift3uiscrollview

Programmatically display UIScrollView in Swift


I have view controller with views: top level View Contains Container View which contains two views: bottomView, topView, as shown.

view controller

Scene

scene

I want to display from: to: date range in the topView. In the bottomView I want to display a UIScrollView that contains two columns which I can scroll. I did that but the topView and BottomView overlap when I introduce scrollView. When I scroll I can see the views getting separated and as soon as i Let go the scrollbar they overlap again.

enter image description here

can someone tell me how to fix it? I just don't seem to understand how the scrollView and bottomView are to be associated. Code below:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.frame = view.bounds

    //scrollView.frame = innerView.bounds
    innerView.frame =   CGRect(x:0, y:0, width:scrollView.contentSize.width, height:scrollView.contentSize.height)
}
func buildBottomView () {
    let screenSize = UIScreen.main.bounds
    let screenWidth = screenSize.width
    let ht:Int = 21
    let incrX:Int = 5
    let incrY:Int = 5
    let gapCol1:Int = 5
    let col1Width:Int = 65
    let col2Width:Int = 65
    let startY:Int = 5
    let col1StartX:Int = 10
    let col2StartX:Int = col1StartX + col1Width + gapCol1

    var loadRowStartY: Int = 0
    // column headers
    categoryColumnLabel.text = "Interval"
    categoryColumnLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.subheadline)
    //categoryColumnLabel.font = UIFont.systemFont(ofSize: 14)
    categoryColumnLabel.frame = CGRect(x: col1StartX, y:startY, width: col1Width, height: ht)
    categoryColumnLabel.textAlignment = NSTextAlignment.left
    categoryColumnLabel.tag = 1
    innerView.addSubview(categoryColumnLabel)

    valueColumnLabel.text = "Values"
    valueColumnLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.subheadline)
    //valueColumnLabel.font = UIFont.systemFont(ofSize: 14)
    valueColumnLabel.frame = CGRect(x: col2StartX, y:startY, width: col2Width, height: ht)
    valueColumnLabel.textAlignment = NSTextAlignment.center
    valueColumnLabel.tag = 3
    innerView.addSubview(valueColumnLabel)

    let sepLine:UIView = UIView()
    sepLine.frame = CGRect(x: col1StartX, y:startY+ht+incrY, width: Int(screenWidth-20), height: 2)
    sepLine.backgroundColor = UIColor.darkGray
    sepLine.tag = 60

    loadRowStartY = startY+ht+incrX+ht
    innerView.addSubview(sepLine)



    for i in 0 ..< 24 {

        let timeIntervalLabel = UILabel()
        let value2Label = UILabel()

        print("display load profile")

        let loadStruct  = loadDict[String(i)] as! CommercialProfile
        print (loadStruct.timeInterval)

        timeIntervalLabel.text = loadStruct.timeInterval
        timeIntervalLabel.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.caption1)

        //valueColumnLabel.font = UIFont.systemFont(ofSize: 14)
        timeIntervalLabel.frame = CGRect(x: col1StartX, y:loadRowStartY, width: col1Width, height: Int(timeIntervalLabel.font.lineHeight))
        timeIntervalLabel.textAlignment = NSTextAlignment.center
        innerView.addSubview(timeIntervalLabel)

        print(loadStruct.value)

        value2Label.text = loadStruct.value
        value2Label.font = UIFont.preferredFont(forTextStyle:UIFontTextStyle.caption1)
        //value2Label = UIFont.systemFont(ofSize: 14)
        value2Label.frame = CGRect(x: col2StartX, y:loadRowStartY, width: col2Width, height: Int(value2Label.font.lineHeight))
        value2Label.textAlignment = NSTextAlignment.center
        innerView.addSubview(value2Label)

        loadRowStartY = loadRowStartY + incrY + Int(value2Label.font.lineHeight)

    }

Solution

  • you are setting the bounds of the scrollView to the size of the whole view with this code: scrollView.frame = view.bounds.

    The scrollView only needs to scroll the content in the bottom view. Scroll Views have their own content, that is normally larger than the viewable area of the screen/view. The scroll view just allows you to pan the viewport of that view.

    So add the bottom view and setup your constraints on that. add the scrollView to the bottom view and then add your content into the scrollView.

    Make sure that your bottom view has clipToBounds set to true and then you should be able to keep the headers in place and just scroll the content.

    I'll try and put an example together for you shortly.

    EDIT:

    I've just created this simple example which shows the scroll behaviour you need. This works in a playground or just as a simple view controller. I've intentionally not used auto layout or setup constraints due to time, but you will see what you need to solve your issue

    class ViewController: UIViewController {
    
        var topView: UIView!
        var bottomView: UIView!
        var scrollView: UIScrollView!
        var contentView: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let screenSize = UIScreen.main.bounds
    
            self.topView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: 100))
            self.bottomView = UIView(frame: CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height - 100))
            self.scrollView = UIScrollView(frame: CGRect(x: 0, y: 100, width: screenSize.width, height: screenSize.height - 100))
            self.contentView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height * 3))
    
            self.view.backgroundColor = .white
            self.view.addSubview(self.topView)
            self.view.addSubview(self.bottomView)
            self.bottomView.addSubview(self.scrollView)
            self.scrollView.addSubview(self.contentView)
            self.bottomView.clipsToBounds = true
    
    
            self.scrollView.contentSize = CGSize(width: screenSize.width, height: screenSize.height * 3)
            self.contentView.backgroundColor = .gray
        }
    }