SwiftUI Scrolling Control from Parent View: How to Scroll to Bottom?

Andrew Ho
2 min readDec 18, 2020

After typing in message and pressing the “Send” button, the keyboard disappears and the message display should now scroll up to show the newly sent message, which is typically at the very bottom of the scrollView right above the text field.

Scrolling Up Programmatically is easy. Understanding it is big step towards knowing how SwiftUI works.

SwiftUI recently added a scrollView.scrollTo method, which moves view to show arbitrary row: scrollView.scrollTo(self.fetchRequest.wrappedValue[self.fetchRequest.wrappedValue.endIndex — 1], anchor:.bottom)

This is tremendously helpful and with additional few ingredients forms an elegant solution.

I organized below the 7 necessary steps, backwards from the actual scrolling to how the scrolling gets triggered by the parent view. I think this is the best way to appreciate how the system works and be able to debug if anything goes wrong:

  1. Each row of the ScrollView must have a “.id(index)” — without this tag, the scrollTo method will not know where to scroll to. Unfortunately, if we leave this step out, there will be no compile or run-time error, just that the scrollView will be move at all when scrollTo gets triggered.
  2. “.onChange” attached to the loop, inside the scrollView — this is the final piece of the listener that will activate the scrollTo action. This has to be inside the scrollView in order to be able to call the scrollView.scrollTo method.

3. ObservedObject variable for the .onChange to monitor — this is how .onChange gets triggered. Easiest is to use a boolean, which can be toggled to cause the desired scrolling to bottom. For example:

.onChange(of: state.message) {

scrollView.scrollTo(20, anchor:.bottom)

}

At the top of the file, declare “@ObservedObject var state:ScrollTrigger”

4. ScrollTrigger class needs to be defined:

class ScrollTrigger: ObservableObject {

@Published var message: Bool = false

func update() {

message.toggle()

}}

5. In the parent view, also add an ObservedObject, so that it can be passed the the child view for purpose of sending an “update” toggle:

@ObservedObject var state = ScrollTrigger()

6. When including the child view in the parent view, make sure that this “state” variable is shared with the child:

ThisIsTheChildView(state:state)

In this way, the two variables “state” in the parent and child views are connected, such that changing the value “state” in the parent will trigger the “onChange” in the child view.

7. All done, by calling “self.state.update()” in the parent view, the scrolling in the child view will be triggered.

--

--