【问题标题】:Layer masking based on positioning iOS基于iOS定位的图层蒙版
【发布时间】:2013-08-10 05:21:06
【问题描述】:

我从未专门在 iOS 上使用过动画,我希望能指出正确的方向。到目前为止,我已经以类似于下面的方式为一堆药丸形状的对象设置了动画,但是我还没有根据形状的 y 位置实现正确的遮罩。

药丸形状只是 UIView。我正在寻找一种方法来根据他们的定位改变他们的背景;背景本身不动,只是药丸。

编辑:

根据 Rob 在下面的回答,我现在将动画与设备的运动联系起来,如下所示:

- (void)setAnimationToDeviceMotion {
    motionManager = [[CMMotionManager alloc] init];
    motionManager.deviceMotionUpdateInterval = 0.1f;
    NSOperationQueue *motionQueue = [[NSOperationQueue alloc] init];

    [motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical toQueue:motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
        if (!error) {
            double pitch = motion.attitude.pitch;
            NSLog(@"%f", pitch);
            [self addMaskToView:self.imageView withAdjustment:pitch];
        }
    }];
}

【问题讨论】:

    标签: ios animation core-animation quartz-graphics quartz-core


    【解决方案1】:

    假设这是八个药丸形状,下面会显示一个图像,你

    • 为八个药丸形状创建一个UIBezierPath
    • 创建一个使用UIBezierPath(或更准确地说,它的CGPath)的CAShapeLayer
    • 将此CAShapeLayer 应用为mask 用于layer 用于您的UIImageView

    如果您想随着时间的推移对其进行动画处理,只需替换 mask(例如,响应加速度计事件,CADisplayLinkNSTimer,或 UIPanGesture),或仅更新 path CAShapeLayer 中的 CAShapeLayer 作为蒙版应用到 UIImageView 的图层。

    因此,您可能会遇到以下情况:

    - (void)addMaskToView:(UIView *)view withAdjustment:(CGFloat)adjustment
    {
        CAShapeLayer *mask = [CAShapeLayer layer];
        mask.path = [[self pillsPathWithAdjustment:adjustment forView:view] CGPath];
    
        self.imageView.layer.mask = mask;
    }
    
    - (UIBezierPath *)pillsPathWithAdjustment:(CGFloat)adjustment forView:(UIView *)view
    {
        UIBezierPath *path = [UIBezierPath bezierPath];
    
        for (NSInteger i = 0; i < kNumberOfPills; i++)
            [self addPillPathNumber:i forView:view toPath:path adjustment:adjustment];
    
        return path;
    }
    
    - (void)addPillPathNumber:(NSInteger)index forView:(UIView *)view toPath:(UIBezierPath *)path adjustment:(CGFloat)adjustment
    {
        CGFloat const pillSpacing = 5.0f;
        CGFloat const pillWidth = view.bounds.size.width / kNumberOfPills - pillSpacing;
        CGFloat const pillHeight =  view.bounds.size.height * 0.4;
        CGFloat const cornerRounding = 10.0f;
    
        CGPoint point = CGPointMake(index * (pillWidth + pillSpacing) + pillSpacing / 2.0, sinf((float) index * M_PI / kNumberOfPills + adjustment * M_PI * 2.0) * 100.0);
    
        // top
    
        point.x += cornerRounding;
        [path moveToPoint:point];
        point.x += pillWidth - cornerRounding * 2.0;
        [path addLineToPoint:point];
    
        // top right corner
    
        [path addArcWithCenter:CGPointMake(point.x, point.y + cornerRounding) radius:cornerRounding startAngle:3 * M_PI_2 endAngle:2.0 * M_PI clockwise:YES];
        point.x += cornerRounding;
        point.y += cornerRounding;
    
        // right
    
        point.y += pillHeight - cornerRounding * 2.0;
        [path addLineToPoint:point];
    
        // lower right corner
    
        [path addArcWithCenter:CGPointMake(point.x - cornerRounding, point.y) radius:cornerRounding startAngle:0 endAngle:M_PI_2 clockwise:YES];
    
        // bottom
    
        point.y += cornerRounding;
        point.x -= cornerRounding;
        point.x -= pillWidth - cornerRounding * 2.0;
        [path addLineToPoint:point];
    
        // lower left corner
    
        [path addArcWithCenter:CGPointMake(point.x, point.y - cornerRounding) radius:cornerRounding startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
    
        // left
    
        point.y -= cornerRounding;
        point.x -= cornerRounding;
        point.y -= pillHeight - cornerRounding * 2.0;
        [path addLineToPoint:point];
    
        // upper left corner
    
        [path addArcWithCenter:CGPointMake(point.x + cornerRounding, point.y) radius:cornerRounding startAngle:M_PI endAngle:3.0 * M_PI_2 clockwise:YES];
    
        [path closePath];
    }
    

    作为说明,这就是我使用CADisplayLink 制作动画的方式:

    - (void)startDisplayLink
    {
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
        self.startTime = CACurrentMediaTime();
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    - (void)stopDisplayLink
    {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    - (void)handleDisplayLink:(CADisplayLink *)displayLink
    {
        CGFloat seconds;
        CGFloat fractionsOfSecond = modff(CACurrentMediaTime() - self.startTime, &seconds);
    
        [self addMaskToView:self.imageView withAdjustment:fractionsOfSecond];
    }
    

    【讨论】:

    • 该死的。这是我对自己关于 SO 的问题之一所看到的最彻底的答案。谢谢罗伯。现在开始实施。
    • Rob,我已经完美地完成了这一切。从字面上看,不必进行任何更改。你如何建议我在酒吧中实施加速度计运动?我希望创建的特定运动是使最低的药丸形状与加速度计的位置相对应。如果我一直向左倾斜,最左边的药丸是最低的。如果我在中间,最中心的药丸是最低的。我在想一个显示链接的组合,它根据加速度计的位置发送适当的调整值。想法?
    • 知道了。我已经将CMMotionManager 与处理程序块放在了一起。我应该如何修改motion.attitude.roll 弧度值以使其成为可接受的adjustment 参数?我是否需要修改计算正弦位置的行?
    • Rob,我已经更新了这个问题,试图让事情正确连接,但是我在动画中没有看到任何反映。有时它会跳到一个新的位置,但没有任何可预测的时机或优雅。
    • 如果我后台应用程序并返回,药丸与设备的位置正确对齐,这让我相信某处存在一些绘图问题。在 imageView 上调用 setNeedsDisplay 没有任何改变。
    【解决方案2】:

    要进行遮罩,您需要使用视图的图层。与其上下移动视图本身,不如创建占据父级整个高度的视图,然后在药丸形状中创建一个CALayer。将药丸层设置为视图层的蒙版,并上下移动。

    类似这样的:

    UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redAndBlueBackground"]];
    
    CALayer *pillLayer = [[CALayer alloc] initWithFrame:CGRectMake(0, 0, myImageView.bounds.size.width, myImageView.bounds.size.height / 2];
    pillLayer.cornerRadius = 8;
    myImageView.layer.maskLayer = pillLayer;
    
    // move the pill layer to height y:
    pillLayer.center = CGPointMake(myImageView.bounds.size.width / 2, 
                                   (myImageView.bounds.size.height / 2) + y);
    

    【讨论】:

    • 所以图像应该是动画区域的全高和药丸的宽度,对吗?
    猜你喜欢
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    • 2014-06-23
    • 1970-01-01
    • 2019-11-25
    • 1970-01-01
    相关资源
    最近更新 更多