Search code examples
swiftxcodenulluiscrollview

Why can't access the image tint color from inside the scrollViewDidScroll


I have the following code:

class SegmentedControlViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var happyOutlet: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        scrollView?.delegate = self
    
    }
    
   func scrollViewDidScroll(_ scrollView: UIScrollView) {
       
       if scrollView.contentOffset.x > 490 {
              
        happyOutlet?.imageView?.tintColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
       }
       else {
           print("nope")
       }
   }

    
    @IBAction func happy(_ sender: UIButton) {

        happyOutlet.imageView?.tintColor = #colorLiteral(red: 0.9294117647, green: 0.4705882353, blue: 0.07450980392, alpha: 1)

    }
}

When I click the button the image tint color changes, although when I scroll more than 450 the tint color doesn't change. I know that my code is right because inside the if statement I tried printing something and it worked so it's something with the button outlet I guess. Also unless I unwrap the happyOutlet in the if statement I get the unwrap optional is nil error.

EDIT

I changed my func to this:

   func scrollViewDidScroll(_ scrollView: UIScrollView) {
       
           if scrollView.contentOffset.x > 20 {
           print("greater than 20")
           happyOutlet?.imageView?.tintColor = .red
       }
       else {
           print("less than 21")
           happyOutlet?.imageView?.tintColor = .green
       }
   }

Although still same thing just the color isn't changing at all, although the stuff gets printed to the console. Also if I don't add the question marks I get the error. What am I doing wrong?


Solution

  • Edit 2

    Take a look at: https://github.com/DonMag/DidScrollTest

    Simple example, plus your project with lots of edits.


    Edit

    After getting more detail and looking at your sample project...

    You have an instance of SegmentedControlViewController which uses a UIContainerView to load your "scroll view" controller. However, your have also assign SegmentedControlViewController as the class for that controller.

    The two controllers in your Storyboard have different elements connected via @IBOutlet. This will not work (as you have seen).

    To accomplish your design, you need a different class for your "scroll view" controller, and you'll want to use a custom delegate / protocol pattern to communicate between them.

    As a side note: It would serve you well to learn some of the fundamentals.

    In a number of places in your code, you use:

    happyView?.alpha = 1
    

    However, that ? means:

    If the variable preceding it is Nil, DON'T do what comes after it.

    So, you're avoiding nil errors by telling the code not to execute, but that doesn't help when you want it to execute.

    When you write this:

    happyView.alpha = 1
    

    and your app crashes with Unexpectedly found nil..., you need to fix that - not edit your code to ignore it.


    Assuming:

    • you have a scroll view connected to @IBOutlet var scrollView: UIScrollView!
    • you have a button, connected to @IBOutlet var happyOutlet: UIButton!
    • you have an image set for the button

    This will change the tint color of the button's image view between red and green, as you scroll 20-pts left and back right. As you scroll, it will also print() the scroll view's content offset x and a message whether it is greater than or less than 20:

    class SegmentedControlViewController: UIViewController {
        
        @IBOutlet var scrollView: UIScrollView!
        @IBOutlet var happyOutlet: UIButton!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // assign the delegate
            scrollView.delegate = self
            
        }
        
    }
    
    extension SegmentedControlViewController: UIScrollViewDelegate {
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
    
            print("content offset x:", scrollView.contentOffset.x)
            
            // if we've scrolled left more than 20 points
            if scrollView.contentOffset.x > 20 {
                print("greater than 20")
                happyOutlet.imageView?.tintColor = .red
            }
            else {
                print("less than 21")
                happyOutlet.imageView?.tintColor = .green
            }
    
        }
    }
     
    

    Note:

    If the happyOutlet button is a subview of the scroll view, in case it's not obvious, the button must be more than 20-pts from the leading edge of the scroll view... otherwise you won't see it change when it is scrolled out of view.