Search code examples
iosswiftuikitscrollviewcontentview

When I click somewhere other than the button in ContentView, it goes to a different place


In my application, I created a scrollView and set the width of this scrollView to twice the width of the screen. Then I created two contentViews. (contentView1 and contentView2) I use these contentViews as two screens. When I click on the "YENİ KAYIT" button in contentView1, the scrollView moves forward and contentView2 comes. The problem with contentView2 is that if I click anywhere other than the Stop button, it goes to an irrelevant place.

You can click here for detailed video : https://github.com/krmdmr7/ProgrammaticScrollView/assets/120031527/a873700b-9bd9-466c-b9b4-8670a1e8604a

You can click here for detailed code : https://github.com/krmdmr7/ProgrammaticScrollView

My UI Views :

    private var allRecordsScrollView: UIScrollView = {
        let allRecordsScrollView = UIScrollView()
        allRecordsScrollView.isPagingEnabled = true
        allRecordsScrollView.contentInsetAdjustmentBehavior = .never
        allRecordsScrollView.showsHorizontalScrollIndicator = false
        allRecordsScrollView.translatesAutoresizingMaskIntoConstraints = false
        allRecordsScrollView.contentSize = CGSize(width: UIScreen.main.bounds.width * 2, height: UIScreen.main.bounds.height)
        return allRecordsScrollView
    }()
    
    private var contentView1: UIView = {
        let contentView1 = UIView()
        contentView1.translatesAutoresizingMaskIntoConstraints = false
        contentView1.backgroundColor = UIColor(named: "LightGreen")
        return contentView1
    }()
    
    private var contentView2: UIView = {
        let contentView2 = UIView()
        contentView2.translatesAutoresizingMaskIntoConstraints = false
        contentView2.backgroundColor = UIColor(named: "Green")
        contentView2.isHidden = true
        return contentView2
    }()

Life Cycle :

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        configureUI()
    }

UI Görünümleri Kısıtlamaları :

    private func configureUI(){
        view.addSubview(allRecordsScrollView)
        NSLayoutConstraint.activate([
            allRecordsScrollView.topAnchor.constraint(equalTo: view.topAnchor),
            allRecordsScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            allRecordsScrollView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 2),
            allRecordsScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            
        ])
        // ContentViews constraints for allRecordsScrollView
        allRecordsScrollView.addSubview(contentView1)
        allRecordsScrollView.addSubview(contentView2)
        NSLayoutConstraint.activate([
            // contentView1 constraints for scrollView
            contentView1.topAnchor.constraint(equalTo: allRecordsScrollView.topAnchor),
            contentView1.bottomAnchor.constraint(equalTo: allRecordsScrollView.bottomAnchor),
            contentView1.leadingAnchor.constraint(equalTo: allRecordsScrollView.leadingAnchor, constant: 0),
            contentView1.widthAnchor.constraint(equalTo: allRecordsScrollView.widthAnchor, multiplier: 0.5),
            contentView1.heightAnchor.constraint(equalTo: allRecordsScrollView.heightAnchor),
            
            // contentView2 constraints for scrollView
            contentView2.topAnchor.constraint(equalTo: allRecordsScrollView.topAnchor),
            contentView2.bottomAnchor.constraint(equalTo: allRecordsScrollView.bottomAnchor),
            contentView2.leadingAnchor.constraint(equalTo: contentView1.trailingAnchor),
            contentView2.widthAnchor.constraint(equalTo: allRecordsScrollView.widthAnchor, multiplier: 0.5),
            contentView2.heightAnchor.constraint(equalTo: allRecordsScrollView.heightAnchor)
        ])
}

Button Functions :

    @objc func recordButtonTapped() {
        print("Record button tapped.")
        contentView2.isHidden = false
        contentView1.isHidden = true
        let xOffset = allRecordsScrollView.frame.width / 2
        allRecordsScrollView.setContentOffset(CGPoint(x: xOffset, y: 0), animated: false)
    }
    
    @objc func stopRecordButtonTapped(){
        contentView2.isHidden = true
        contentView1.isHidden = false
        print("Stop record button tapped.")
        allRecordsScrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
    }


Solution

  • You are making things very overcomplicated by using a UIScrollView ... and you're using it incorrectly anyway.

    A much easier approach is to:

    • add your "bubble" image as the first subview of the controller's view
    • set background color to .clear for both contentView1 and contentView2... that will allow the "bubble" image to "show through"
    • to view contentView1, show contentView1 and hide contentView2
    • to view contentView2, show contentView2 and hide contentView1

    Here is a modified version of your complete ViewController class from your posted GitHub project:

    class ViewController: UIViewController {
        
        // MARK : UI Elements
        private var contentView1: UIView = {
            let contentView1 = UIView()
            contentView1.translatesAutoresizingMaskIntoConstraints = false
            // clear
            //contentView1.backgroundColor = UIColor(named: "LightGreen")
            contentView1.backgroundColor = .clear
            return contentView1
        }()
        
        private var contentView2: UIView = {
            let contentView2 = UIView()
            contentView2.translatesAutoresizingMaskIntoConstraints = false
            // clear
            //contentView2.backgroundColor = UIColor(named: "Green")
            contentView2.backgroundColor = .clear
            return contentView2
        }()
        
        private let kendinYapLabel:UILabel = {
            let kendinYapLabel = UILabel()
            kendinYapLabel.translatesAutoresizingMaskIntoConstraints = false
            kendinYapLabel.font = UIFont(name: "Overpass-Bold", size: 24.0)
            kendinYapLabel.text = "KENDİN YAP"
            kendinYapLabel.textColor = .white
            return kendinYapLabel
        }()
        
        lazy var backButton: UIButton = {
            let backButton = UIButton()
            backButton.translatesAutoresizingMaskIntoConstraints = false
            backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
            backButton.setImage(UIImage(named: "backIcon"), for: .normal)
            backButton.setTitleColor(UIColor.white, for: .normal)
            return backButton
        }()
        
        private let bigGreenTopBubbleImageView: UIImageView = {
            let bigGreenTopBubbleImageView = UIImageView()
            let image = UIImage(named: "bigGreenTopBubble")
            bigGreenTopBubbleImageView.translatesAutoresizingMaskIntoConstraints = false
            bigGreenTopBubbleImageView.image = image
            bigGreenTopBubbleImageView.contentMode = .scaleAspectFill
            return bigGreenTopBubbleImageView
        }()
        
        lazy var recordButton:UIButton = {
            let recordButton = UIButton()
            let image = UIImage(named: "recordButtonBGAll")
            recordButton.translatesAutoresizingMaskIntoConstraints = false
            recordButton.addTarget(self, action: #selector(recordButtonTapped), for: .touchUpInside)
            recordButton.setImage(image, for: .normal)
            return recordButton
        }()
        
        private let recorderVoicesLabel:UILabel = {
            let recorderVoicesLabel = UILabel()
            recorderVoicesLabel.translatesAutoresizingMaskIntoConstraints = false
            recorderVoicesLabel.text = "KAYITLI SESLER"
            recorderVoicesLabel.font = UIFont(name: "Overpass-Bold", size: 20.0)
            recorderVoicesLabel.textColor = .white
            return recorderVoicesLabel
        }()
        
        public var recordCounterLabel:UILabel = {
            let recordCounterLabel = UILabel()
            recordCounterLabel.translatesAutoresizingMaskIntoConstraints = false
            recordCounterLabel.font = UIFont(name: "Overpass-Regular", size: 100.0)
            recordCounterLabel.text = "00:00"
            recordCounterLabel.textColor = .white
            return recordCounterLabel
        }()
        
        public var recordingStatusLabel:UILabel = {
            let recordingStatusLabel = UILabel()
            recordingStatusLabel.translatesAutoresizingMaskIntoConstraints = false
            recordingStatusLabel.font = UIFont(name: "Overpass-Regular", size: 20.0)
            recordingStatusLabel.text = "Ses kaydı bekleniyor."
            recordingStatusLabel.textColor = .white
            return recordingStatusLabel
        }()
        
        lazy var stopRecordButton:UIButton = {
            let stopRecordButton = UIButton()
            let image = UIImage(named: "recordButtonStopIcon")
            stopRecordButton.translatesAutoresizingMaskIntoConstraints = false
            stopRecordButton.addTarget(self, action: #selector(stopRecordButtonTapped), for: .touchUpInside)
            stopRecordButton.setImage(image, for: .normal)
            return stopRecordButton
        }()
        
        
        // MARK : Life Cycle
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // set view background color
            view.backgroundColor = UIColor(named: "LightGreen")
            
            configure()
        }
        
        private func configure(){
            configureUI()
        }
        
        
        // MARK : Setup UI
        private func configureUI(){
            
            // add bigGreenTopBubbleImageView as the first subview
            //  the "content" views will have clear backgrounds,
            //  so we see the "bubble" image through them
            view.addSubview(bigGreenTopBubbleImageView)
            NSLayoutConstraint.activate([
                // BigGreenBubble constraints
                bigGreenTopBubbleImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: -70),
                bigGreenTopBubbleImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
                bigGreenTopBubbleImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
                bigGreenTopBubbleImageView.heightAnchor.constraint(equalToConstant: 466),
            ])
            
            // add the two "content" views
            view.addSubview(contentView1)
            view.addSubview(contentView2)
            NSLayoutConstraint.activate([
                
                // constrain both "content" views to fill the view
                //  contentView2 will be "overlaid on top of" contentView1
                
                contentView1.topAnchor.constraint(equalTo: view.topAnchor),
                contentView1.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                contentView1.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                contentView1.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                
                contentView2.topAnchor.constraint(equalTo: view.topAnchor),
                contentView2.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                contentView2.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                contentView2.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                
            ])
            
            // add contentView1's subviews
            contentView1.addSubview(backButton)
            contentView1.addSubview(kendinYapLabel)
            contentView1.addSubview(recordButton)
            contentView1.addSubview(recorderVoicesLabel)
            NSLayoutConstraint.activate([
                // constrain subviews to contentView1, NOT to view
                // Back button constraints
                backButton.topAnchor.constraint(equalTo: contentView1.topAnchor, constant: 48),
                backButton.leadingAnchor.constraint(equalTo: contentView1.leadingAnchor, constant: 20),
                backButton.widthAnchor.constraint(equalToConstant: 50),
                backButton.heightAnchor.constraint(equalToConstant: 54),
                // KendinYapLabel constraints
                kendinYapLabel.centerXAnchor.constraint(equalTo: contentView1.centerXAnchor),
                kendinYapLabel.centerYAnchor.constraint(equalTo: backButton.centerYAnchor),
                // RecordButton constraints
                recordButton.topAnchor.constraint(equalTo: kendinYapLabel.bottomAnchor, constant: 55),
                recordButton.leadingAnchor.constraint(equalTo: contentView1.leadingAnchor, constant: 91),
                recordButton.trailingAnchor.constraint(equalTo: contentView1.trailingAnchor, constant: -91),
                recordButton.heightAnchor.constraint(equalToConstant: 77),
                // RecorderVoicesLabel constraints
                recorderVoicesLabel.topAnchor.constraint(equalTo: recordButton.bottomAnchor, constant: 20),
                recorderVoicesLabel.leadingAnchor.constraint(equalTo: contentView1.leadingAnchor, constant: 43),
                recorderVoicesLabel.heightAnchor.constraint(equalToConstant: 25.5)
            ])
            
            // add contentView2's subviews
            contentView2.addSubview(recordCounterLabel)
            contentView2.addSubview(recordingStatusLabel)
            contentView2.addSubview(stopRecordButton)
            NSLayoutConstraint.activate([
                // constrain subviews to contentView2, NOT to view
                // Record counter label constraints
                recordCounterLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
                recordCounterLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                // Recording status label constraints
                recordingStatusLabel.topAnchor.constraint(equalTo: recordCounterLabel.bottomAnchor, constant: 10),
                recordingStatusLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                // Stop record button constraints
                stopRecordButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -132),
                stopRecordButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                stopRecordButton.widthAnchor.constraint(equalToConstant: 118),
                stopRecordButton.heightAnchor.constraint(equalToConstant: 118)
            ])
    
            // start with contentView2 hidden
            contentView2.isHidden = true
        }
        
        
        // MARK : Functions
        
        // MARK : Actions
        @objc func recordButtonTapped() {
            print("Record button tapped.")
            // hide contentView1, show contentView2
            contentView1.isHidden = true
            contentView2.isHidden = false
        }
        
        @objc func backButtonTapped() {
            print("Back button tapped.")
        }
        
        @objc func stopRecordButtonTapped(){
            print("Stop record button tapped.")
            // show contentView1, hide contentView2
            contentView1.isHidden = false
            contentView2.isHidden = true
        }
        
    }