8000 [FEAT] Respond to moving the digital crown on the Apple Watch · Issue #265 · fermoya/SwiftUIPager · GitHub
[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] Respond to moving the digital crown on the Apple Watch #265

Closed
andriy-appuchino opened this issue Apr 1, 2022 · 25 comments
Closed
Labels
enhancement New feature or request
Milestone

Comments

@andriy-appuchino
Copy link

Hello. Great job!
But I ran into a problem that the pager does not respond to moving the digital crown on the Apple Watch, as this usually works with ScrollView. Can I somehow enable digital crown support?

@andriy-appuchino andriy-appuchino added the bug Something isn't working label Apr 1, 2022
@fermoya fermoya added enhancement New feature or request and removed bug Something isn't working labels Apr 1, 2022
@fermoya fermoya changed the title [BUG] Does not respond to moving the digital crown on the Apple Watch [FEAT] Respond to moving the digital crown on the Apple Watch Apr 1, 2022
@fermoya
Copy link
Owner
fermoya commented Apr 1, 2022

Hi @andriy-appuchino. I don't think I ever considered the Digital Crown but it should be simple enough. I'll take a look at it next week

@andriy-appuchino
Copy link
Author

@fermoya I haven't been able to find a solution, so I'll be waiting to see if you manage to implement it. Thanks a lot.

@andriy-appuchino
Copy link
Author

By the way, I just tried to do something myself, I managed to get it to work, but it works a little clunky, not like the native scroll on watchOS.

	@StateObject var page: Page = .first()
	var items = Array(0..<10)
	@State var scrollAmount = 0.0
	
	var body: some View {
		Pager(page: page,
				  data: items,
				  id: \.self,
				  content: { index in
					  // create a page based on the data passed
					  Text("Page: \(index)")
			 })
		.vertical()
		.focusable(true)
		.digitalCrownRotation($scrollAmount, from: 0, through: 10, by: 1, sensitivity: .low, isContinuous: false, isHapticFeedbackEnabled: true)
		.onChange(of: scrollAmount) { newValue in
			withAnimation {
				page.update(.new(index: Int(newValue)))
			}
		}
	}

@fermoya
Copy link
Owner
fermoya commented Apr 4, 2022

@andriy-appuchino I'm not sure how this would work. digitalCrownRotation isn't like a DragGesture that has an onEnded callback. Therefore, you can leave Pager stuck in between 2 pages. This works great with a ScrollView but Pager isn't just a ScrollView. See video below for more info:

Simulator.Screen.Recording.-.Apple.Watch.Series.7.-.45mm.-.2022-04-04.at.17.30.56.mp4

@andriy-appuchino
Copy link
Author

@fermoya Look at my example, the important part is to set from: 0, through: 10, by: 1. Where the through value is the number of pages.
When rotating the crown and the value is fractional, you need to move the pages in the same way as you did with the drag gesture. But when you stop rotating the crown, the value will automatically go to a non-fractional value, which will work like onEded, and will already be stopped on a specific page.

@andriy-appuchino
Copy link
Author
andriy-appuchino commented Apr 4, 2022

@fermoya could you try please?

@fermoya
Copy link
Owner
fermoya commented Apr 4, 2022

Oh I see now what you mean. I'll come back to you shortly

@fermoya
Copy link
Owner
fermoya commented Apr 5, 2022

@andriy-appuchino check out 2.4.0-beta.1. Notice that this works with watchOS 7.0 onwards

@fermoya fermoya added this to the 2.4.0 milestone Apr 5, 2022
@andriy-appuchino
Copy link
Author
andriy-appuchino commented Apr 5, 2022

@fermoya Thank you for your efforts. I tried it, but it works about the same as my example. I'll try to clarify.
Now when the crown rotates, the content does not move, but only after stopping, with some delay. It's not like the digital crown works on a watch.
My expectation is that when rotating the crown, the content will also move, as if you are using a drag gesture, and then it centers the page.
Could you improve this?

Here I made an example to demonstrate the logic I mean:

Simulator.Screen.Recording.-.Apple.Watch.Series.7.-.45mm.-.2022-04-05.at.18.21.50.mp4
	var body: some View {
		GeometryReader { proxy in
			List {
				Group {
					Color.blue
					Color.green
					Color.yellow
					Color.orange
					Color.red
				}
				.listRowBackground(Color.clear)
				.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
				.frame(height: proxy.size.height)
			}
			.listStyle(.elliptical)
		}
	}

You can try this code for yourself. If you do not have an Apple Watch, then on the watch simulator a digital crown rotating is triggered by moving two fingers.

@fermoya
Copy link
Owner
fermoya commented Apr 5, 2022

@andriy-appuchino I've been testing in a simulator, didn’t you see my first video? I haven’t merely copy-pasted your example if that’s what you suggest 😅

digitalCrownRotation doesn’t work the same as DragGesture as you imply. DragGesture has an onEnded callback (or alternatively, a GestureState that resets). digitalCrownRotation doesn’t, it just updated a Binding. There’s no way to know when the rotation finished. So to ensure the page is centered I can just update the page index.

Any suggestions are more than welcome but this is the best it can be done as far as I can see.

Feel free to fork the repo, peek the code (PagerContent.body), make changes and send a PR.

@andriy-appuchino
Copy link
Author

Well, yes, that's right. When the value changes to a fractional, then you need to move the content as you do with a drag gesture, and when the value becomes a non-fractional number, then just consider it fired onEnded.

@fermoya
Copy link
Owner
fermoya commented Apr 5, 2022

print the value observed in onChanged, it's always a fractional! What you suggest is already done. Whenever the rotation goes over 0.5, page index is incremented/decremented.

All in all, it can't be done in a linear fashion so to speak, unfortunately. An onEnded callback or any other workaround would be needed

@andriy-appuchino
Copy link
Author

OK, thank you. I already made a fork, I'll try to figure it out. I'll let you know if it works.

@fermoya
Copy link
Owner
fermoya commented Apr 5, 2022

Thanks @andriy-appuchino , all help is much appreciated. Keep me posted, I'd like to make a release within the next days. If you find some possible improv 8000 ement I'll include it

@andriy-appuchino
Copy link
Author

@fermoya I managed to do it, I can't wait for you to try #268

@fermoya
Copy link
Owner
fermoya commented Apr 7, 2022

new 2.4.0-beta.2 pushed

@andriy-appuchino
Copy link
Author

Great, it works as it should! 🎉
Thank you.

@andriy-appuchino
Copy link
Author

@fermoya by the way, when will the release be?

@fermoya
Copy link
Owner
fermoya commented Apr 7, 2022

I'll make a release within 2-3 days tops most. I want to see if I can resolve an issue with a pipeline, first.

Anyway, I'll make a comment here when the new version is out

@andriy-appuchino
Copy link
Author
andriy-appuchino commented Apr 7, 2022

@fermoya I caught a bug.

Initial:

  1. Two pages
  2. We are on page 1

Steps:

  1. Swipe to page 2
  2. Rotate the crown in the same direction as if you wanted to go to page 3 (which doesn't exist)

Expectations:
Nothing should happen.

Result:
The page bounces, the position indicator on the right shows movement from page 1 to page 2.

@andriy-appuchino
Copy link
Author
andriy-appuchino commented Apr 7, 2022

After switching the page with a swipe, you need to change the state of the digital crown digitalCrownPageOffset, you apparently missed it. It was in my PR, you can see how I did it.

@fermoya
Copy link
Owner
fermoya commented Apr 8, 2022

Should be solved in beta-4

@andriy-appuchino
Copy link
Author
andriy-appuchino commented Apr 8, 2022

There was another problem on watchOS. If Pager is in TabView like in an example, then switching between tabs from left to right very often does not work. Probably Pager is blocking gestures horizontally somewhere.

struct ContentView: View {
	enum Tab {
		case controls, main, player
	}
	
	@State private var selection: Tab = .main
	
	@StateObject var page: Page = .first()
	var items = Array(0..<2)
	
	var body: some View {
		TabView(selection: $selection) {
			ControlsView()
				.tag(Tab.controls)
			
			Pager(page: page,
				  data: items,
				  id: \.self,
				  content: { index in
				Text("Page: \(index)")
			})
			.vertical()
			.tag(Tab.main)
			
			NowPlayingView()
				.tag(Tab.player)
		}
		.tabViewStyle(PageTabViewStyle())
	}
}

@fermoya
Copy link
Owner
fermoya commented Apr 8, 2022

@andriy-appuchino please open a new ticket, I think it's unrelated with this thread.

seems to be working for me anyways:

Simulator.Screen.Recording.-.Apple.Watch.Series.7.-.45mm.-.2022-04-08.at.15.08.16.mp4

@fermoya fermoya closed this as completed Apr 8, 2022
@andriy-appuchino
Copy link
Author
andriy-appuchino commented Apr 10, 2022

@fermoya When there is only Text on the page, the problem is less noticeable because the swipe is blocked only in the text area. I created an issue #274 and attached a project where the pages are filled with color. There is a very high reproducibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants
0