Created
July 28, 2024 02:53
-
-
Save DandyLyons/e95af09ad40a8a7e9ee9bb04931fca3e to your computer and use it in GitHub Desktop.
ProgrammaticScrollView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| import Foundation | |
| struct ProgrammaticScrollView<Content: View>: View { | |
| @Binding private var scrollID: Int? | |
| let content: () -> Content | |
| init(scrollID: Binding<Int?>, @ViewBuilder content: @escaping () -> Content) { | |
| self._scrollID = scrollID | |
| self.content = content | |
| } | |
| var body: some View { | |
| ScrollView { | |
| content() | |
| .padding(.horizontal) | |
| .background { | |
| VStack { | |
| ForEach(1..<101) { num in | |
| VStack { | |
| Text("\(num)").frame(maxWidth: .infinity, alignment: .leading) | |
| .id(num) | |
| .opacity(0.0) | |
| Spacer() | |
| } | |
| } | |
| } | |
| } | |
| } | |
| .scrollPosition(id: $scrollID, anchor: .top) | |
| .animation(.default, value: self.scrollID) | |
| .task(id: "autoscroll") { | |
| scrollID = 1 | |
| await scroll(by: 1, every: .seconds(1)) | |
| } | |
| .safeAreaInset(edge: .bottom) { | |
| if scrollID != 1 { | |
| Button("Scroll to top", systemImage: "arrow.up") { | |
| scrollID = 1 | |
| } | |
| .containerRelativeFrame(.horizontal, alignment: .leading) | |
| .labelStyle(.iconOnly) | |
| .buttonStyle(CircleButton(background: .blue)) | |
| .padding(.leading) | |
| } | |
| if scrollID != 100 { | |
| Button("Scroll to bottom", systemImage: "arrow.down") { | |
| scrollID = 100 | |
| } | |
| .containerRelativeFrame(.horizontal, alignment: .trailing) | |
| .labelStyle(.iconOnly) | |
| .buttonStyle(CircleButton(background: .blue)) | |
| .padding(.trailing) | |
| } | |
| } | |
| } | |
| // this function demonstrates how your model can scroll the view to any position it wants at any time. | |
| @MainActor | |
| func scroll(by percentage: Int, every duration: ContinuousClock.Instant.Duration) async { | |
| while true { | |
| guard let oldValue = scrollID else { scrollID = 0; continue } | |
| switch oldValue { | |
| case Int.min..<0: scrollID = 0 | |
| case 0..<100: scrollID = oldValue + percentage | |
| case 100..<Int.max: scrollID = 100 | |
| default: scrollID = 0 | |
| } | |
| _ = try? await Task.sleep(for: duration) | |
| } | |
| } | |
| } | |
| struct CircleButton<Background: ShapeStyle>: ButtonStyle { | |
| let background: Background | |
| func makeBody(configuration: Configuration) -> some View { | |
| configuration.label | |
| .padding() | |
| .background(background, in: .circle) | |
| } | |
| } | |
| struct ExampleView: View { | |
| @State private var scrollPercentage: Int? = 1 | |
| @State private var picker = 34 | |
| var body: some View { | |
| ProgrammaticScrollView(scrollID: $scrollPercentage) { | |
| Text(verbatim: whosOnFirst) | |
| } | |
| .safeAreaInset(edge: .bottom) { | |
| bottomBar | |
| } | |
| } | |
| @ViewBuilder var bottomBar: some View { | |
| VStack { | |
| HStack { | |
| Picker("Select a number", selection: $picker) { | |
| ForEach(0..<100) { num in | |
| Text("\(num)").id(num) | |
| } | |
| } | |
| Button("Scroll to \(picker)%") { scrollPercentage = picker } | |
| } | |
| LabeledContent("scrollPercentage", value: "\(scrollPercentage)") // useful for debugging | |
| } | |
| .padding(.horizontal) | |
| .background(.thinMaterial, ignoresSafeAreaEdges: .bottom) | |
| } | |
| } | |
| #Preview { | |
| ExampleView() | |
| } | |
| let whosOnFirst = """ | |
| “WHO'S ON FIRST?” | |
| The text of Abbott & Costello's classic routine. | |
| Abbott: Well, Costello, I'm going to New York with you. Bucky Harris the Yankee's manager gave me a job as | |
| coach for as long as you're on the team. | |
| Costello: Look Abbott, if you're the coach, you must know all the players. | |
| Abbott: I certainly do. | |
| Costello: Well you know I've never met the guys. So you'll have to tell me their names, and then I'll know who's | |
| playing on the team. | |
| Abbott: Oh, I'll tell you their names, but you know it seems to me they give these ball players now-a-days very | |
| peculiar names. | |
| Costello: You mean funny names? | |
| Abbott: Strange names, pet names...like Dizzy Dean... | |
| Costello: His brother Daffy | |
| Abbott: Daffy Dean... | |
| Costello: And their French cousin. | |
| Abbott: French? | |
| Costello: Goofe' | |
| Abbott: Goofe' Dean. Well, let's see, we have on the bags, Who's on first, What's on second, I Don't Know is on | |
| third... | |
| Costello: That's what I want to find out. | |
| Abbott: I say Who's on first, What's on second, I Don't Know's on third. | |
| Costello: Are you the manager? | |
| Abbott: Yes. | |
| Costello: You gonna be the coach too? | |
| Abbott: Yes. | |
| Costello: And you don't know the fellows' names. | |
| Abbott: Well I should. | |
| Costello: Well then who's on first? | |
| Abbott: Yes. | |
| Costello: I mean the fellow's name. | |
| Abbott: Who. | |
| Costello: The guy on first. | |
| Abbott: Who. | |
| Costello: The first baseman. | |
| Abbott: Who. | |
| Costello: The guy playing... | |
| Abbott: Who is on first! | |
| Costello: I'm asking you who's on first. | |
| Abbott: That's the man's name. | |
| Costello: That's who's name? | |
| Abbott: Yes. | |
| Costello: Well go ahead and tell me. | |
| Abbott: That's it. | |
| Costello: That's who? | |
| Abbott: Yes. PAUSE | |
| Costello: Look, you gotta first baseman? | |
| Abbott: Certainly. | |
| Costello: Who's playing first? | |
| Abbott: That's right. | |
| Costello: When you pay off the first baseman every month, who gets the money? | |
| Abbott: Every dollar of it. | |
| Costello: All I'm trying to find out is the fellow's name on first base. | |
| Abbott: Who. | |
| Costello: The guy that gets... | |
| Abbott: That's it. | |
| Costello: Who gets the money... | |
| Abbott: He does, every dollar of it. Sometimes his wife comes down and collects it. | |
| Costello: Who's wife? | |
| Abbott: Yes. PAUSE | |
| Abbott: What's wrong with that? | |
| Costello: I wanna know is when you sign up the first baseman, how does he sign his name? | |
| Abbott: Who. | |
| Costello: The guy. | |
| Abbott: Who. | |
| Costello: How does he sign... | |
| Abbott: That's how he signs it. | |
| Costello: Who? | |
| Abbott: Yes. PAUSE | |
| Costello: All I'm trying to find out is what's the guys name on first base. | |
| Abbott: No. What is on second base. | |
| Costello: I'm not asking you who's on second. | |
| Abbott: Who's on first. | |
| Costello: One base at a time! | |
| Abbott: Well, don't change the players around. | |
| Costello: I'm not changing nobody! | |
| Abbott: Take it easy, buddy. | |
| Costello: I'm only asking you, who's the guy on first base? | |
| Abbott: That's right. | |
| Costello: OK. | |
| Abbott: Alright. PAUSE | |
| Costello: What's the guy's name on first base? | |
| Abbott: No. What is on second. | |
| Costello: I'm not asking you who's on second. | |
| Abbott: Who's on first. | |
| Costello: I don't know. | |
| Abbott: He's on third, we're not talking about him. | |
| Costello: Now how did I get on third base? | |
| Abbott: Why you mentioned his name. | |
| Costello: If I mentioned the third baseman's name, who did I say is playing third? | |
| Abbott: No. Who's playing first. | |
| Costello: What's on base? | |
| Abbott: What's on second. | |
| Costello: I don't know. | |
| Abbott: He's on third. | |
| Costello: There I go, back on third again! PAUSE | |
| Costello: Would you just stay on third base and don't go off it. | |
| Abbott: Alright, what do you want to know? | |
| Costello: Now who's playing third base? | |
| Abbott: Why do you insist on putting Who on third base? | |
| Costello: What am I putting on third. | |
| Abbott: No. What is on second. | |
| Costello: You don't want who on second? | |
| Abbott: Who is on first. | |
| Costello: I don't know. Together: Third base! PAUSE | |
| Costello: Look, you gotta outfield? | |
| Abbott: Sure. | |
| Costello: The left fielder's name? | |
| Abbott: Why. | |
| Costello: I just thought I'd ask you. | |
| Abbott: Well, I just thought I'd tell ya. | |
| Costello: Then tell me who's playing left field. | |
| Abbott: Who's playing first. | |
| Costello: I'm not...stay out of the infield!!! I want to know what's the guy's name in left field? | |
| Abbott: No, What is on second. | |
| Costello: I'm not asking you who's on second. | |
| Abbott: Who's on first! | |
| Costello: I don't know. Together: Third base! PAUSE | |
| Costello: The left fielder's name? | |
| Abbott: Why. | |
| Costello: Because! | |
| Abbott: Oh, he's center field. PAUSE | |
| Costello: Look, You gotta pitcher on this team? | |
| Abbott: Sure. | |
| Costello: The pitcher's name? | |
| Abbott: Tomorrow. | |
| Costello: You don't want to tell me today? | |
| Abbott: I'm telling you now. | |
| Costello: Then go ahead. | |
| Abbott: Tomorrow! | |
| Costello: What time? | |
| Abbott: What time what? | |
| Costello: What time tomorrow are you gonna tell me who's pitching? | |
| Abbott: Now listen. Who is not pitching. | |
| Costello: I'll break you're arm if you say who's on first!!! I want to know what's the pitcher's name? | |
| Abbott: What's on second. | |
| Costello: I don't know. Together: Third base! PAUSE | |
| Costello: Gotta a catcher? | |
| Abbott: Certainly. | |
| Costello: The catcher's name? | |
| Abbott: Today. | |
| Costello: Today, and tomorrow's pitching. | |
| Abbott: Now you've got it. | |
| Costello: All we got is a couple of days on the team. PAUSE | |
| Costello: You know I'm a catcher too. | |
| Abbott: So they tell me. | |
| Costello: I get behind the plate to do some fancy catching, Tomorrow's pitching on my team and a heavy hitter | |
| gets up. Now the heavy hitter bunts the ball. When he bunts the ball, me, being a good catcher, I'm gonna throw | |
| the guy out at first. So I pick up the ball and throw it to who? | |
| Abbott: Now that's the first thing you've said right. | |
| Costello: I don't even know what I'm talking about! PAUSE | |
| Abbott: That's all you have to do. | |
| Costello: Is to throw the ball to first base. | |
| Abbott: Yes! | |
| Costello: Now who's got it? | |
| Abbott: Naturally. PAUSE | |
| Costello: Look, if I throw the ball to first base, somebody's gotta get it. Now who has it? | |
| Abbott: Naturally. | |
| Costello: Who? | |
| Abbott: Naturally. | |
| Costello: Naturally? | |
| Abbott: Naturally. | |
| Costello: So I pick up the ball and I throw it to Naturally. | |
| Abbott: No you don't you throw the ball to Who. | |
| Costello: Naturally. | |
| Abbott: That's different. | |
| Costello: That's what I said. | |
| Abbott: you're not saying it... | |
| Costello: I throw the ball to Naturally. | |
| Abbott: You throw it to Who. | |
| Costello: Naturally. | |
| Abbott: That's it. | |
| Costello: That's what I said! | |
| Abbott: You ask me. | |
| Costello: I throw the ball to who? | |
| Abbott: Naturally. | |
| Costello: Now you ask me. | |
| Abbott: You throw the ball to Who? | |
| Costello: Naturally. | |
| Abbott: That's it. | |
| Costello: Same as you! Same as YOU!!! I throw the ball to who. Whoever it is drops the ball and the guy runs to | |
| second. Who picks up the ball and throws it to What. What throws it to I Don't Know. I Don't Know throws it back | |
| to Tomorrow, Triple play. Another guy gets up and hits a long fly ball to Because. Why? I don't know! He's on third | |
| and I don't give a darn! | |
| Abbott: What? | |
| Costello: I said I don't give a darn! | |
| Abbott: Oh, that's our shortstop. | |
| Costello: (makes screaming sound) | |
| """ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment