title: Scroll info from SwiftUI List - UIViewRepresentable UIScrollView List bindings not always working with SwiftUI
body: <p>I'm trying to observe/get some of SwiftUI's List scrolling attributes by wrapping/injection of UIScrollView using UIViewRepresentable.</p>
  <p>I'm getting inconsistent behavior with bindings.</p>
  <p>Pressing the buttons and changing values depending on if the button is in the parent vs child view has different results.<br /><br /></p>
  <p>The bindings from my ObservableObject ScrollInfo class and the UIViewRepresentable start fine, but then break, unless the whole screen is refreshed and makeUIView runs again (like changing to a different tab).</p>
  <p>Is there a way to force the UIViewRepresentable to run makeUIView again on a binding update? Or something that will fix this?
  <br /><br /></p>
  <p>I'd like for isScrolling values to be updated and working all the time.</p>
  <p>I set up a test to change the colors of the circles to red if the user is dragging the scrollview down. It works initially but stops if I update a value from the ObservableObject in the parent view.<br /><br /></p>
  <p><strong>Screenshots of Test from code below</strong></p>
  <p><a href="https://i.stack.imgur.com/zV0L9.jpg" rel="nofollow noreferrer">Bindings keep working with bottom button press (updating ObservableObject) in child view</a></p>
  <p><a href="https://i.stack.imgur.com/bVh8Q.jpg" rel="nofollow noreferrer">Bindings break with top button press (updating ObservableObject) in parent view</a></p>
  <br />
  <pre><code>// Parent View
  
  import SwiftUI
  
  struct ContentView: View {
      @ObservedObject var scrollInfo:ScrollInfo = ScrollInfo()
      var body: some View {
    VStack
                 {
                     Button(action:{
                      self.scrollInfo.contentLoaded = true;
                         
                     })
                     {
                         Text(&quot;REFRESH&quot;)
                     }
               TestView()
               
             }
      }
  }
  
  struct ContentView_Previews: PreviewProvider {
      static var previews: some View {
          ContentView()
      }
  }
  
  // Child View
  
  
  import SwiftUI
  
  struct TestView: View {
  
       @State var test1:String = &quot;Test1&quot;
      @ObservedObject var scrollInfo:ScrollInfo = ScrollInfo()
      
      var body: some View {
          VStack
              {
                  Button(action:{
                      self.scrollInfo.contentLoaded.toggle()
                                 })  {
                                               Text(&quot;REFRESH&quot;)
                                           }
          
          List{
              VStack  {
  
                 
                  VStack{
                      Text(String( self.scrollInfo.contentLoaded))
                           Text(self.test1)
                           Circle().frame(width:50,height:50).foregroundColor(self.scrollInfo.isScrolling ? .red : .green)
                  }
                  VStack{
                           Text(self.scrollInfo.text)
                           Text(self.test1)
                           Circle().frame(width:50,height:50).foregroundColor(self.scrollInfo.isScrolling ? .red : .green)
                  }
                  
                  
                           VStack{
                                    Text(self.scrollInfo.text)
                                    Text(self.test1)
                                    Circle().frame(width:50,height:50).foregroundColor(self.scrollInfo.isScrolling ? .red : .green)
                           }
                           
                  
                           VStack{
                                    Text(self.scrollInfo.text)
                                    Text(self.test1)
                                    Circle().frame(width:50,height:50).foregroundColor(self.scrollInfo.isScrolling ? .red : .green)
                           }
                           
                  
              }   .padding(.bottom,620).padding(.top,20)
            .background(
              ListScrollingHelper(scrollInfo: self.scrollInfo)// injection
              )
          }.padding(.top,4).onAppear(perform:{
              
          })
          }
          
      }
  }
  
  struct TestView_Previews: PreviewProvider {
      static var previews: some View {
          TestView()
      }
  }
  
  
  // ScrollInfo Class
  
  
  
  class ScrollInfo: ObservableObject {
  
      @Published var isScrolling:Bool = false
      @Published var text:String = &quot;Blank&quot;
      @Published var contentLoaded:Bool = false
  
      init()
      {
          print( &quot;scrollInfo init&quot;)
      }
      deinit {
          print(&quot;scrollInfo denit&quot;)
      }
  
  }
  
  // UIViewRepresentable
  
  
  final class ListScrollingHelper: UIViewRepresentable {
      var scrollInfo:ScrollInfo
      @Published var scrollView: UIScrollView?
      
      init( scrollInfo:ScrollInfo) {
          print(&quot;init UIViewRepresentable listscrolling helper&quot;)
          self.scrollInfo = scrollInfo
          
      }
      
      func makeUIView(context: Context) -&gt; UIView {
          
          //self.uiScrollView.delegate = context.coordinator
          print(&quot;makeview&quot;)
          return UIView()
          //return self.uiScrollView // managed by SwiftUI, no overloads
      }
      func catchScrollView(for view: UIView) {
          print(&quot;checking for scrollview&quot;)
          if nil == scrollView {
              scrollView = view.enclosingScrollView()
              if(scrollView != nil)
              {
                  print(&quot;scrollview found&quot;)
              }
          }
      }
      func updateUIView(_ uiView: UIView, context: Context) {
          
         
        catchScrollView(for: uiView)
          if(scrollView != nil)
                  {
            scrollView!.delegate = context.coordinator
      }
          
          print(&quot;updatingUIView&quot;)
          
      }
     
      func makeCoordinator() -&gt; Coordinator {
          Coordinator(self)
                
      }
      class Coordinator: NSObject,UIScrollViewDelegate {
          
          var parent: ListScrollingHelper
  
          init(_ listScrollingHelper: ListScrollingHelper) {
              self.parent = listScrollingHelper
              print(&quot;init coordinator&quot;)
          }
  
          func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
                    // DispatchQueue.main.async {
                         if(!self.parent.scrollInfo.isScrolling)
                         {
                     self.parent.scrollInfo.isScrolling = true
                     //self.parent.scrollInfo.text = &quot;scroll&quot;
                         //       }
                     }
                     print(&quot;start scroll&quot;)
              
          }
          
          func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
          
              if(self.parent.scrollInfo.isScrolling &amp;&amp; !decelerate)
                                   {
                    self.parent.scrollInfo.isScrolling = false
                    }
                     print(&quot;end scroll&quot;)
                    
                       }
          func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
              if(self.parent.scrollInfo.isScrolling)
                             {
              self.parent.scrollInfo.isScrolling = false
              }
               print(&quot;end scroll&quot;)
              
                 }
          
             deinit
             {
                 print(&quot;deinit coordinator&quot;)
             }
       
  
      }
      
      deinit {
            print(&quot;deinit UIViewRepresentable listscrolling helper&quot;)
      }
  }
  extension UIView {
      func enclosingScrollView() -&gt; UIScrollView? {
          var next: UIView? = self
          repeat {
              next = next?.superview
              if let scrollview = next as? UIScrollView {
                  return scrollview
              }
          } while next != nil
          return nil
      }
  }
  
  </code></pre>
  <p><strong>EDIT - WORKAROUND</strong></p>
  <p>I wasn't able to get it working with the ObservableObject or EnvironmentObject, but I was able to get it working with @State and @Binding, although it's a limited amount of info passed back. (ScrollInfo is still there only to use to testing changing a parent ObservableObject)</p>
  <p>Hope this helps someone else!</p>
  <pre><code>import SwiftUI
  
  struct TestView: View {
  
      @State var isScrolling:Bool = false;
      var body: some View {
          VStack
              {
                  Button(action:{
                      self.scrollInfo.contentLoaded.toggle()
                                 })  {
                                               Text(&quot;REFRESH&quot;)
                                           }
          
          List{
              VStack  {
  
                 
                  VStack{
                      Text(&quot;isScrolling&quot;)
                      Text(String(self.isScrolling))
                           Circle().frame(width:50,height:50).foregroundColor(self.isScrolling ? .red : .green)
                  }
                  VStack{
                           Text(self.scrollInfo.text)
                           Text(self.test1)
                           Circle().frame(width:50,height:50).foregroundColor(self.isScrolling ? .red : .green)
                  }
                  
                  
                           VStack{
                                    Text(self.scrollInfo.text)
                                    Text(self.test1)
                                    Circle().frame(width:50,height:50).foregroundColor(self.isScrolling ? .red : .green)
                           }
                           
                  
                           VStack{
                                    Text(self.scrollInfo.text)
                                    Text(self.test1)
                                    Circle().frame(width:50,height:50).foregroundColor(self.isScrolling ? .red : .green)
                           }
                           
                  
                  }   .padding(.bottom,620).padding(.top,20).background( ListScrollingHelper(isScrolling: self.$isScrolling))
              
          }.padding(.top,4)
          
      }
  }
  }
  
  extension UIView {
      func enclosingScrollView() -&gt; UIScrollView? {
          var next: UIView? = self
          repeat {
              next = next?.superview
              if let scrollview = next as? UIScrollView {
                  return scrollview
              }
          } while next != nil
          return nil
      }
  }
  
  struct TestView_Previews: PreviewProvider {
      static var previews: some View {
          TestView()
      }
  }
  
  
  final class ListScrollingHelper: UIViewRepresentable {
      @Binding var isScrolling:Bool
      private var scrollView: UIScrollView?
      
      init(isScrolling:Binding&lt;Bool&gt;)
      {
          self._isScrolling = isScrolling
      }
      
      func makeUIView(context: Context) -&gt; UIView {
          return UIView()
      }
      func catchScrollView(for view: UIView) {
          if nil == scrollView {
              scrollView = view.enclosingScrollView()
          }
      }
      func updateUIView(_ uiView: UIView, context: Context) {
          
         
        catchScrollView(for: uiView)
          if(scrollView != nil)
                  {
            scrollView!.delegate = context.coordinator
      }
          
      }
     
      func makeCoordinator() -&gt; Coordinator {
          Coordinator(self)
                
      }
      class Coordinator: NSObject,UIScrollViewDelegate {
          
          var parent: ListScrollingHelper
  
          init(_ listScrollingHelper: ListScrollingHelper) {
              self.parent = listScrollingHelper
          }
  
          func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
                         if(!self.parent.isScrolling)
                         {
                     self.parent.isScrolling = true
                     }
              
          }
          
          func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
          
              if(self.parent.isScrolling &amp;&amp; !decelerate)
                                   {
                    self.parent.isScrolling = false
                    }
                    
                       }
          func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
              if(self.parent.isScrolling)
                             {
              self.parent.isScrolling = false
              }
                 }
          
             deinit
             {
             }
       
  
      }
      
      deinit {
      }
  }
  
  
  
  class ScrollInfo: ObservableObject {
  
      @Published var isScrolling:Bool = false
      @Published var text:String = &quot;Blank&quot;
      @Published var contentLoaded:Bool = false
  
  }
  
  </code></pre>