Можно ли расположить виды друг на друге

Я создаю приложение Watch, в котором я хочу наложить WKInterfaceImage на группу с кучей объектов WKInterfaceLabel. Кажется, я не могу сделать это в редакторе StoryBoard.

Кто-нибудь когда-либо был в состоянии добиться расположения друг над другом представлений для Watch App?

PS. Мне известно о методе WKInterfaceGroup setBackgroundImage. Так как я хочу сделать некоторую анимацию внутри WKInterfaceImage, setBackgroundImage не будет горе для меня

2 ответа

Вы не можете расположить объекты WKInterfaceObject друг над другом. Вы можете сделать надписи над изображением, а затем установить его. Вам придется визуализировать метки для каждого кадра анимации. Я делаю это в своем приложении для часов. Я генерирую изображение, которое является большой частью моего пользовательского интерфейса, затем я генерирую каждый кадр анимации и накладываю текст кнопки для каждого кадра. Затем разрезать изображение для каждого кадра, чтобы я мог анимацию на каждой из кнопок. Когда вы используете приложение, создается впечатление, что изображение анимируется под кнопками, а на самом деле это 4 разные анимации в 4 разных кнопках.

редактировать

Я подумал, что хотел бы добавить еще немного деталей. Вот снимок экрана моего приложения. Я предполагаю, что вы хотите сделать что-то подобное.

Сначала в моей раскадровке нужно убедиться, что интервал для ваших групп равен нулю.

Для создания этого интерфейса я использую этот вспомогательный класс. Я отредактировал этот класс, чтобы сосредоточиться только на необходимых частях.

typedef struct
{
    CGRect top;
    CGRect left;
    CGRect right;
    CGRect bottom;
} ButtonRects;

@interface GPWatchMapImage ()

@property (readwrite, nonatomic) UIImage* topImage;
@property (readwrite, nonatomic) UIImage* bottomImage;
@property (readwrite, nonatomic) UIImage* leftImage;
@property (readwrite, nonatomic) UIImage* rightImage;

@property (readwrite, nonatomic) CGRect topRect;
@property (readwrite, nonatomic) CGRect bottomRect;
@property (readwrite, nonatomic) CGRect leftRect;
@property (readwrite, nonatomic) CGRect rightRect;


@property (readwrite, nonatomic) UIImage* fullImageWithoutArrows;

@property BOOL addedArrows;


@end

@implementation GPWatchMapImage

-(instancetype)init
{
    self = [super init];
    if (self)
    {

    }
    return self;
}



-(UIImage*)leftImage
{
    if (_leftImage == nil)
    {
        [self breakUpForButtons];
    }
    return _leftImage;
}

-(UIImage*)rightImage
{
    if (_rightImage == nil)
    {
        [self breakUpForButtons];
    }
    return _rightImage;
}

-(UIImage*)topImage
{
    if (_topImage == nil)
    {
        [self breakUpForButtons];
    }
    return _topImage;
}

-(UIImage*)bottomImage
{
    if (_bottomImage == nil)
    {
        [self breakUpForButtons];
    }
    return _bottomImage;
}

-(UIImage*)fullImageWithoutArrows
{
    [self fullImage]; //make sure we have the full image
    if (_fullImageWithoutArrows != nil)
    {
        return _fullImageWithoutArrows;
    }
    return _fullImage;
}

-(UIImage*)fullImage
{
    if (_fullImage == nil)
    {
        //This is the rect to create the image in
        CGRect rect = CGRectMake(0, 0, self.watchSize.width, self.watchSize.height);

        //This is how I generate map images.  You will need to do something else
        self.generatedMapInfo = [[GPCustomMapMaker instance] makeCustomMapFromConfig:self.mapConfig];
        _fullImage = self.generatedMapInfo.mapImage;

    }

    if (self.showMapArrows && !self.addedArrows)
    {
        //Add the arrows
        [self addButtonArrowsToFullImage];
    }


    return _fullImage;
}


-(void)addButtonArrowsToFullImage
{
    self.addedArrows = YES;

    ButtonRects rects = [self buttonRects];
    UIImage* img = self.fullImage;
    self.fullImageWithoutArrows = img;  //save for animations
    UIGraphicsBeginImageContext(img.size);

    UIColor* color = [UIColor colorWithRed:.4 green:.4 blue:.4 alpha:.6];

    //CGSize arrowSize = CGSizeMake(24, 4);
    CGSize arrowSize = CGSizeMake(48, 8);
    CGFloat edgeOffset = 26;
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(ctx, 6);
    CGContextSetLineJoin(ctx, kCGLineJoinRound);
    CGContextSetLineCap(ctx, kCGLineCapRound);
    CGContextSetStrokeColorWithColor(ctx, color.CGColor);

    [img drawAtPoint:CGPointMake(0, 0)];


    //Left arrow
    CGPoint leftCenter = CGPointMake(rects.left.origin.x + edgeOffset, rects.left.origin.y + rects.left.size.height/2);
    CGContextBeginPath(ctx);
    CGContextMoveToPoint(ctx, leftCenter.x + arrowSize.height, leftCenter.y - arrowSize.width/2);
    CGContextAddLineToPoint(ctx, leftCenter.x, leftCenter.y);
    CGContextAddLineToPoint(ctx, leftCenter.x + arrowSize.height, leftCenter.y + arrowSize.width/2);
    CGContextStrokePath(ctx);

    CGPoint rightCenter = CGPointMake(rects.right.origin.x + rects.right.size.width - edgeOffset, rects.right.origin.y + rects.right.size.height/2);
    CGContextBeginPath(ctx);
    CGContextMoveToPoint(ctx, rightCenter.x, rightCenter.y - arrowSize.width/2);
    CGContextAddLineToPoint(ctx, rightCenter.x + arrowSize.height, rightCenter.y);
    CGContextAddLineToPoint(ctx, rightCenter.x, rightCenter.y + arrowSize.width/2);
    CGContextStrokePath(ctx);


    CGPoint topCenter = CGPointMake(rects.top.origin.x + rects.top.size.width/2, rects.top.origin.y + edgeOffset);
    CGContextBeginPath(ctx);
    CGContextMoveToPoint(ctx, topCenter.x - arrowSize.width/2, topCenter.y + arrowSize.height);
    CGContextAddLineToPoint(ctx, topCenter.x, topCenter.y);
    CGContextAddLineToPoint(ctx, topCenter.x + arrowSize.width/2, topCenter.y + arrowSize.height);
    CGContextStrokePath(ctx);

    CGPoint bottomCenter = CGPointMake(rects.bottom.origin.x + rects.bottom.size.width/2, rects.bottom.origin.y + rects.bottom.size.height - edgeOffset);
    CGContextBeginPath(ctx);
    CGContextMoveToPoint(ctx, bottomCenter.x - arrowSize.width/2, bottomCenter.y);
    CGContextAddLineToPoint(ctx, bottomCenter.x, bottomCenter.y + arrowSize.height);
    CGContextAddLineToPoint(ctx, bottomCenter.x + arrowSize.width/2, bottomCenter.y);
    CGContextStrokePath(ctx);


    UIImage* imgWithButtons = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    _fullImage = imgWithButtons;
}

-(UIImage*)subImageInRect:(CGRect)rect fromImage:(UIImage*)image
{
    UIGraphicsBeginImageContext(rect.size);

    [image drawInRect:CGRectMake(-rect.origin.x, -rect.origin.y, image.size.width, image.size.height)];

    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

-(ButtonRects)buttonRects
{
    UIImage* img = self.fullImage;
    CGSize size = self.watchSize;
    CGFloat topHeight = size.height * .25;
    CGFloat bottomHeight = size.height * .25;
    CGFloat middleHeight = size.height * .5;

    CGRect topRect = CGRectMake(0, 0, size.width, topHeight);
    CGRect leftRect = CGRectMake(0, topHeight, img.size.width/2.0, middleHeight);
    CGRect rightRect = CGRectMake(img.size.width/2.0, topHeight, img.size.width/2.0, middleHeight);
    CGRect bottomRect = CGRectMake(0, topHeight + middleHeight, size.width, bottomHeight);

    ButtonRects rects;
    rects.top = topRect;
    rects.bottom = bottomRect;
    rects.left = leftRect;
    rects.right = rightRect;

    return rects;
}

-(void)breakUpForButtons
{
    UIImage* img = self.fullImage;
    ButtonRects rects = [self buttonRects];

    _topImage = [self subImageInRect:rects.top fromImage:img];
    _leftImage = [self subImageInRect:rects.left fromImage:img];
    _rightImage = [self subImageInRect:rects.right fromImage:img];
    _bottomImage = [self subImageInRect:rects.bottom fromImage:img];
}

@end

Затем в моем классе WKInterfaceController я делаю это для создания анимации.

typedef NS_ENUM(NSInteger, GPWatchImageAnimation)
{
    GPWatchImageAnimationNone,
    GPWatchImageAnimationSlideLeftToRight,
    GPWatchImageAnimationSlideRighToLeft,
    GPWatchImageAnimationSlideTopToBottom,
    GPWatchImageAnimationSlideBottomToTop,
    GPWatchImageAnimationZoomIn,
    GPWatchImageAnimationZoomOut
};

#define kNumImagesInMapAnimations 6
#define kMapAnimatinonDuration 0.4

...

-(void)updateMapImageWithAnimation:(GPWatchImageAnimation)animation
{
    GPWatchMapImage* previousImage = self.currentMapImage;

    //This gets the size of the full image that you want    
    CGSize size = [self mapSize];
    self.currentMapImage = [[GPWatchMapImage alloc] init];
    self.currentMapImage.watchSize = size;

        //Check if we have a previous image to animate from
        if (previousImage != nil && animation != GPWatchImageAnimationNone)
        {
            NSDictionary* animatedImage = [self animateFromImage:previousImage toImage:self.currentMapImage withAnimation:animation];
            [self.mapTopImage setImage:[animatedImage objectForKey:@"top"]];
            [self.mapLeftImage setImage:[animatedImage objectForKey:@"left"]];
            [self.mapRightImage setImage:[animatedImage objectForKey:@"right"]];
            [self.mapBottomImage setImage:[animatedImage objectForKey:@"bottom"]];

            [self.mapTopImage startAnimatingWithImagesInRange:NSMakeRange(0, kNumImagesInMapAnimations) duration:kMapAnimatinonDuration repeatCount:1];
            [self.mapLeftImage startAnimatingWithImagesInRange:NSMakeRange(0, kNumImagesInMapAnimations) duration:kMapAnimatinonDuration repeatCount:1];
            [self.mapRightImage startAnimatingWithImagesInRange:NSMakeRange(0, kNumImagesInMapAnimations) duration:kMapAnimatinonDuration repeatCount:1];
            [self.mapBottomImage startAnimatingWithImagesInRange:NSMakeRange(0, kNumImagesInMapAnimations) duration:kMapAnimatinonDuration repeatCount:1];
        }
        else
        {
            [self.mapTopImage setImage:self.currentMapImage.topImage];
            [self.mapLeftImage setImage:self.currentMapImage.leftImage];
            [self.mapRightImage setImage:self.currentMapImage.rightImage];
            [self.mapBottomImage setImage:self.currentMapImage.bottomImage];
        }

}

-(NSDictionary*)animateFromImage:(GPWatchMapImage*)fromImage toImage:(GPWatchMapImage*)toImage withAnimation:(GPWatchImageAnimation)animation
{
    NSMutableArray* topAnimatedImages = [[NSMutableArray alloc] initWithCapacity:kNumImagesInMapAnimations];
    NSMutableArray* bottomAnimatedImages = [[NSMutableArray alloc] initWithCapacity:kNumImagesInMapAnimations];
    NSMutableArray* leftAnimatedImages = [[NSMutableArray alloc] initWithCapacity:kNumImagesInMapAnimations];
    NSMutableArray* rightAnimatedImages = [[NSMutableArray alloc] initWithCapacity:kNumImagesInMapAnimations];

    CGSize size = fromImage.fullImage.size;
    for (int step = 0; step < kNumImagesInMapAnimations; step++)
    {
        UIGraphicsBeginImageContext(size);

        //render this step
        if (animation == GPWatchImageAnimationSlideLeftToRight)
        {
            CGFloat stepSize = (size.width/2)/kNumImagesInMapAnimations;
            CGPoint fromPoint = CGPointMake(-(step+1)*stepSize, 0);
            CGPoint toPoint = CGPointMake(size.width/2 - (step+1)*stepSize, 0);
            [fromImage.fullImageWithoutArrows drawAtPoint:fromPoint];
            [toImage.fullImageWithoutArrows drawAtPoint:toPoint];
        }
        else if (animation == GPWatchImageAnimationSlideRighToLeft)
        {
            CGFloat stepSize = (size.width/2)/kNumImagesInMapAnimations;
            CGPoint fromPoint = CGPointMake((step+1)*stepSize, 0);
            CGPoint toPoint = CGPointMake(-size.width/2 + (step+1)*stepSize, 0);
            [fromImage.fullImageWithoutArrows drawAtPoint:fromPoint];
            [toImage.fullImageWithoutArrows drawAtPoint:toPoint];
        }
        else if (animation == GPWatchImageAnimationSlideTopToBottom)
        {
            CGFloat stepSize = (size.height/2)/kNumImagesInMapAnimations;
            CGPoint fromPoint = CGPointMake(0, (step+1)*stepSize);
            CGPoint toPoint = CGPointMake(0, -size.height/2 + (step+1)*stepSize);
            [fromImage.fullImageWithoutArrows drawAtPoint:fromPoint];
            [toImage.fullImageWithoutArrows drawAtPoint:toPoint];
        }
        else if (animation == GPWatchImageAnimationSlideBottomToTop)
        {
            CGFloat stepSize = (size.height/2)/kNumImagesInMapAnimations;
            CGPoint fromPoint = CGPointMake(0, -(step+1)*stepSize);
            CGPoint toPoint = CGPointMake(0, size.height/2 - (step+1)*stepSize);
            [fromImage.fullImageWithoutArrows drawAtPoint:fromPoint];
            [toImage.fullImageWithoutArrows drawAtPoint:toPoint];
        }
        else if (animation == GPWatchImageAnimationZoomOut)
        {
            CGFloat yStepSize = (size.height/2)/kNumImagesInMapAnimations;
            CGFloat xStepSize = (size.width/2)/kNumImagesInMapAnimations;
            //CGRect fromRect = CGRectMake((step + 1)*xStepSize, (step + 1)*yStepSize, size.width - 2*(step + 1)*xStepSize, size.height - 2*(step + 1)*yStepSize);
            CGRect toRect = CGRectMake(-size.width/2 + (step+1)*xStepSize, -size.height/2 + (step+1)*yStepSize, size.width + 2*(kNumImagesInMapAnimations - step - 1)*xStepSize, size.height + 2*(kNumImagesInMapAnimations - step - 1)*yStepSize);
            [toImage.fullImageWithoutArrows drawInRect:toRect];

            //double alpha = (double)(kNumImagesInMapAnimations - step - 1)/(double)kNumImagesInMapAnimations;
            //CGContextSetAlpha(UIGraphicsGetCurrentContext(), alpha);
            //[fromImage.fullImageWithoutArrows drawInRect:fromRect];
            //CGContextSetAlpha(UIGraphicsGetCurrentContext(), 1.0);
        }
        else if (animation == GPWatchImageAnimationZoomIn)
        {
            if (step == kNumImagesInMapAnimations -1)
            {
                [toImage.fullImageWithoutArrows drawAtPoint:CGPointMake(0, 0)];
            }
            else
            {
                CGFloat yStepSize = (size.height/2)/kNumImagesInMapAnimations;
                CGFloat xStepSize = (size.width/2)/kNumImagesInMapAnimations;
                CGRect fromRect = CGRectMake(-(step + 1)*xStepSize, -(step + 1)*yStepSize, size.width + 2*(step + 1)*xStepSize, size.height + 2*(step + 1)*yStepSize);
                //CGRect toRect = CGRectMake(-size.width/2 + (step+1)*xStepSize, -size.height/2 + (step+1)*yStepSize, size.width + 2*(kNumImagesInMapAnimations - step - 1)*xStepSize, size.height + 2*(kNumImagesInMapAnimations - step - 1)*yStepSize);
                [fromImage.fullImageWithoutArrows drawInRect:fromRect];
                //[toImage.fullImageWithoutArrows drawInRect:fromRect];
            }
        }
        else
        {
            [toImage.fullImageWithoutArrows drawAtPoint:CGPointMake(0, 0)];
        }

        UIImage* stepImg = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();


        //Get each of the pieces of the image
        GPWatchMapImage* mapImage = [[GPWatchMapImage alloc] init];
        mapImage.showMapArrows = self.showMapArrows;
        mapImage.fullImage = stepImg;
        mapImage.watchSize = [self mapSize];
        [topAnimatedImages addObject:mapImage.topImage];
        [bottomAnimatedImages addObject:mapImage.bottomImage];
        [leftAnimatedImages addObject:mapImage.leftImage];
        [rightAnimatedImages addObject:mapImage.rightImage];
    }

    UIImage* topAnimatedImage = [UIImage animatedImageWithImages:topAnimatedImages duration:kMapAnimatinonDuration];
    UIImage* bottomAnimatedImage = [UIImage animatedImageWithImages:bottomAnimatedImages duration:kMapAnimatinonDuration];
    UIImage* leftAnimatedImage = [UIImage animatedImageWithImages:leftAnimatedImages duration:kMapAnimatinonDuration];
    UIImage* rightAnimatedImage = [UIImage animatedImageWithImages:rightAnimatedImages duration:kMapAnimatinonDuration];
    return @{@"top": topAnimatedImage, @"bottom": bottomAnimatedImage, @"left": leftAnimatedImage, @"right": rightAnimatedImage};
}

В InterfaceBuilder: вы можете просто использовать группу и установить Backgroundimage из ваших активов. Когда вы затем добавляете WKInterfaceImage в эту группу, у вас есть два слоя для работы (если вам достаточно двух слоев).

Другие вопросы по тегам