如何检查Parallax是否已启用

7

我正在制作一款壁纸应用程序,并希望检查用户是否在其iOS 7设备上启用了视差效果。在objective-C中,有没有一种方法可以检查这个布尔值?苹果公司授权我们开发人员访问此功能吗?

例如,如果启用了视差效果,请执行步骤1,否则执行步骤2。

3个回答

13

截至iOS 8:

// Returns whether the system preference for reduce motion is enabled
UIKIT_EXTERN BOOL UIAccessibilityIsReduceMotionEnabled() NS_AVAILABLE_IOS(8_0);
UIKIT_EXTERN NSString *const UIAccessibilityReduceMotionStatusDidChangeNotification NS_AVAILABLE_IOS(8_0);

对于 iOS 8 及之前的版本,我认为没有一种合法的方法可以确定。


有趣,有没有办法在iOS7上检查这个? - kelin
对于 iOS 8 之前的任何版本,我认为没有合法的方法可以判断。 - John Scalo
任何非法的方式呢? - kelin

5
根据Gabriele的回答,似乎没有直接读取该值的方法。
作为一种解决方法,您可以利用UIInterpolatingMotionEffect的特性,如果运动是默认的,则会执行某些操作,但如果减少运动,则不执行任何操作。
因此,使用自定义UIView类,并在应用程序启动时立即附加一个UIInterpolatingMotionEffect实例。如果更改了属性,则设置标志。稍后检查该标志。
可能还有其他经验性的副作用可以依赖,但这首先假设用户在使用您的应用程序时会移动设备。因此,您将确切地知道他们是否开启了运动,并移动了他们的设备,但否则,您将不知道他们是否关闭了运动或只是没有移动设备。
也许有更聪明的人能想出更好的解决方法?

编辑:示例代码。在评论中讨论的问题是该属性必须是可动画的,这导致需要手动轮询。在您的应用程序中的某个位置添加此视图的实例,最好是在启动时,并使其保持在整个应用程序的生命周期内保持在屏幕上,当然前提是您的视图控制器层次结构允许它。然后观察parallaxHasOccurred属性。它符合KVO,或者您可以进行轮询。正如讨论的那样,它可能会生成错误的负面结果,但永远不会生成错误的正面结果。

@interface PTParallaxTestView : UIView

// this key is KVO compliant
@property (nonatomic, assign) BOOL parallaxHasOccurred;

@end


@implementation PTParallaxTestView
{
    CGPoint _basePosition;
    UIMotionEffectGroup *_effectGroup;
}

- (void)didMoveToSuperview
{
    // cancel any detection loop we may have ongoing
    [NSObject cancelPreviousPerformRequestsWithTarget:self];

    // if anything still in doubt and we're on a view then start the
    // detection loop
    if(!self.parallaxHasOccurred && self.superview)
    {
        // add motion effects if they're not already attached; attach both to the centre property
        if(!_effectGroup)
        {
            UIInterpolatingMotionEffect *horizontalMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
            horizontalMotionEffect.minimumRelativeValue = @(0);
            horizontalMotionEffect.maximumRelativeValue = @(100);

            UIInterpolatingMotionEffect *verticalMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
            verticalMotionEffect.minimumRelativeValue = @(0);
            verticalMotionEffect.maximumRelativeValue = @(100);

            _effectGroup = [[UIMotionEffectGroup alloc] init];
            _effectGroup.motionEffects = @[verticalMotionEffect, horizontalMotionEffect];
            [self addMotionEffect:_effectGroup];
        }

        // kick off inspection in 0.1 seconds; we'll subsequently inspect
        // every 0.5 seconds
        [self performSelector:@selector(beginCheckingPresentationPosition) withObject:nil afterDelay:0.1];
    }
}

- (void)beginCheckingPresentationPosition
{
    // set the base position and do the first check in 0.5 seconds
    _basePosition = [[[self layer] presentationLayer] position];
    [self performSelector:@selector(checkPresentationPosition) withObject:nil afterDelay:0.5];
}

- (void)checkPresentationPosition
{
    // quick note on presentationLayer:
    //
    //  The property supplied to UIInterpolatingMotionEffect must be animatable. So we can't just create our own.
    //  UIKit will then apply effects directly to the layer. Furthermore, the layer itself will act as if in a
    //  perpetual animation so its properties won't directly be affected. We'll have to query the presentationLayer.
    //  (and that's also why we're pulling rather than using KVO or a suitable subclass to push)
    //
    CGPoint newPosition = [[[self layer] presentationLayer] position];

    // if the position has changed since the original test then things are in motion
    if(fabs(newPosition.x - _basePosition.x) > 0.125 || fabs(newPosition.y - _basePosition.y) > 0.125)
        self.parallaxHasOccurred = YES;

    // check again in 0.5 seconds only if we don't already know the answer
    if(!self.parallaxHasOccurred)
        [self performSelector:@selector(checkPresentationPosition) withObject:nil afterDelay:0.5];
}

@end

UIInterpolatingMotionEffect 技巧的发现很不错。+1 - Gabriele Petronella
我必须测试一下,虽然我不知道如何检查 UIInterpolatingMotionEffect 以及我会得到哪些值?有任何示例代码吗? - Sam B
@SamBudda,你不需要检查它,它会将值推送到你给定的任何键路径。这就是为什么你要设置一个虚拟属性,如果它有任何值被设置,它就只是设置一个标志(或者可能是第二次设置值时,为了安全起见)。我稍后会尝试添加一些示例代码,但现在我正在工作中(嘘!) - Tommy
@Tommy,希望你没有忘记我 :-) - Sam B
抱歉,我们正试图在圣诞节前将某些东西放入App Store中,所以很匆忙。回到家后我确实忘了。我会尽量记得今晚... - Tommy
感谢您写下这篇文章:我正在构思通过完全相同的技术来检测它的方法,很高兴发现有人比我先做到了。 - cbowns

1

对于不支持视差效果的设备(例如 iPhone 5 之前的任何 iPhone 型号),您只需检查型号,并确保没有开启视差效果。

对于支持它的设备,您应该以编程方式检查减少动作辅助功能设置,但显然没有公共 API 可以检查该选项是否开启。

根据UIKit 函数参考,您可以执行的唯一检查如下所示

  • UIAccessibilityPostNotification:发布无障碍通知。
  • UIAccessibilityIsVoiceOverRunning:检查屏幕阅读器是否正在运行。
  • UIAccessibilityIsClosedCaptioningEnabled:检查字幕是否启用。
  • UIAccessibilityRequestGuidedAccessSession:请求引导访问会话。
  • UIAccessibilityIsGuidedAccessEnabled:检查引导访问是否启用。
  • UIAccessibilityIsInvertColorsEnabled:检查反转颜色是否启用。
  • UIAccessibilityIsMonoAudioEnabled:检查单声道音频是否启用。
  • UIAccessibilityZoomFocusChanged:缩放焦点发生变化。
  • UIAccessibilityRegisterGestureConflictWithZoom:将手势冲突注册到缩放中。
  • UIAccessibilityConvertFrameToScreenCoordinates:将框架转换为屏幕坐标系。
  • UIAccessibilityConvertPathToScreenCoordinates:将路径转换为屏幕坐标系。

问题在于用户可能使用更新的iPhone 4S和5,但仍然禁用视差效果。我的壁纸应用程序在很大程度上依赖于此检查,否则整个壁纸在视差和非视差情况下看起来都很傻,即我将收到很多投诉性评论。 - Sam B
@SamBudda 我明白你的意思,但是我说没有直接检测它的方法。然而,Tommy的答案看起来有些hackish但很有前途。 - Gabriele Petronella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接