love icon indicating copy to clipboard operation
love copied to clipboard

Support `vibrate(intensity, mode)` for iOS

Open ParticleG opened this issue 2 years ago • 5 comments

On iOS 10.0+, iPadOS 10.0+ and Mac Catalyst 13.1+, it is possible for iPhones and iPads to generate different type of viberations, Not just a fixed 400ms one. Just Mapping seconds to UIFeedbackGenerator's subclasses like this:

if (@available(iOS 10.0, *)) {
	// iOS 10.0 and above
	UIImpactFeedbackGenerator *impact = nil;
	if (seconds >= 0.5 && seconds < 1.5) {
		impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
	} else if (seconds >= 1.5 && seconds < 2.5) {
		impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
	} else if (seconds >= 2.5) {
		impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy];
	} else {
		return;
	}
	[impact impactOccurred];
	return;
}

ParticleG avatar Feb 22 '23 01:02 ParticleG

And iOS13+ seems have even more options, I'll test them later

ParticleG avatar Feb 22 '23 02:02 ParticleG

Add the second param to vibrate method with default value default, while on iOS default means the old fixed 500ms vibration, so it won't affect existing codes:

// src/modules/system/wrap_System.cpp
int w_vibrate(lua_State *L)
{
	double seconds = luaL_optnumber(L, 1, 0.5);
	const char *mode = luaL_optstring(L, 2, "default");
	instance()->vibrate(seconds, mode);
	return 0;
}

And ignore mode param on Android(For now):

// src/modules/system/System.cpp
void System::vibrate(double seconds, const std::string &mode) const
{
#ifdef LOVE_ANDROID
	love::android::vibrate(seconds);
	LOVE_UNUSED(mode);
#elif defined(LOVE_IOS)
	love::ios::vibrate(seconds, mode);
#else
	LOVE_UNUSED(seconds);
	LOVE_UNUSED(mode);
#endif
}

Then process them on iOS:

void vibrate(const double intensity, const std::string &mode)
{
	@autoreleasepool
	{
		if (mode != "default" && @available(iOS 10.0, *)) {
			// iOS 10.0 and above
			UIImpactFeedbackGenerator *impact = nil;
			if (mode == "light") {
				impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
			} else if (mode == "medium") {
				impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
			} else if (mode == "heavy") {
				impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy];
			}
			if (@available(iOS 13.0, *)) {
				// iOS 13.0 and above
				if (mode == "soft") {
					impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleSoft];
				} else if (mode == "rigid") {
					impact = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleRigid];
				}
				if(impact != nil){
					[impact impactOccurredWithIntensity:(intensity < 0 ? 0 : (intensity > 1 ? 1 : intensity))];
					return;
				}
			}
			if(impact != nil){
				[impact impactOccurred];
				return;
			}
		}
		AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
	}
}

ParticleG avatar Feb 22 '23 08:02 ParticleG

Per my comment in the pull request (https://github.com/love2d/love/pull/1905#issuecomment-1445454068), I don't really feel good about having platform-specific function parameters. I wonder if some of Android's haptic feedback APIs are similar enough to the ones you've investigated for iOS that the two backends can both implement a mode parameter.

slime73 avatar Feb 26 '23 20:02 slime73

Android actually has similar "light", "medium" and "heavy" haptic feedbacks. Like EFFECT_TICK, EFFECT_CLICK and EFFECT_HEAVY_CLICK on Android 10+ , while android has much more customization options.

ParticleG avatar Feb 27 '23 03:02 ParticleG

Another solution is continue using simgle seconds parameter, since Android can automatically switch between haptic vibration and normal long vibration depends on how long the vibration is (On my OnePlus 7 Pro with A 13, shorter than 0.3s are haptic vibrations), maybe we could manually set a custom curve to tell iOS when to switch from haptic vibration to long vibration.

ParticleG avatar Feb 27 '23 03:02 ParticleG