按下时使用 UIViewAnimationTransitionFlipFromRight 翻转 UIButton 并在“另一侧”显示新按钮

Posted

技术标签:

【中文标题】按下时使用 UIViewAnimationTransitionFlipFromRight 翻转 UIButton 并在“另一侧”显示新按钮【英文标题】:Using UIViewAnimationTransitionFlipFromRight to flip over a UIButton when pressed and show a new button on "the other side" 【发布时间】:2013-03-04 22:20:00 【问题描述】:

为 MOXY 编辑。

我有一组顶部带有图像的按钮和一组视图为背景色的按钮。当我单击一个按钮时,我想检索该按钮索引并使用该索引来获取另一个视图的适当背景颜色..并使用该信息翻转按钮(带有图像)并“将其翻转”到显示背景颜色。

我的屏幕组成是 ViewControllersView->TileView->(按钮数组)。

 -(void)viewDidAppear:(BOOL)animated
 _viewContents = [model getRequestedView];  //this retrieves an array of UIButtons(24 
  //of them) whose frames and background colors have already been initialized to fit
   //within the TileView.
 _viewFrontImageContents =[model getRequestedViewFrontCardImagesWithFrame:_viewContents];

for(int screenViewIndex = 0; screenViewIndex < [_viewContents count]; screenViewIndex++)
    [TileView addSubview:[_viewFrontImageContents objectAtIndex:screenViewIndex]];
    [[_viewFrontImageContents objectAtIndex:screenViewIndex] addTarget:self action:@selector(frontOfCardPushed:) forControlEvents:UIControlEventTouchUpInside];
    //[TileView addSubview:[_viewContents objectAtIndex:screenViewIndex]];
    //[[_viewContents objectAtIndex:screenViewIndex] addTarget:self action:@selector(frontOfCardPushed:) forControlEvents:UIControlEventTouchUpInside];



//getRequestedViewFrontCardImagesWithFrame方法

-(NSMutableArray *)getRequestedViewFrontCardImagesWithFrame:(NSArray *)views
        NSMutableArray *cards = [[NSMutableArray alloc] init];
        UIImage *frontOfCardImage = [UIImage imageNamed:@"Back_of_Tile"];
        UIButton *frontOfCardView;

        for(int viewIndex = 0; viewIndex < [views count]; viewIndex++)
            frontOfCardView = [[UIButton alloc] initWithFrame:[[views objectAtIndex:viewIndex] frame]];
            frontOfCardView.backgroundColor = [UIColor colorWithPatternImage:frontOfCardImage];
  //setting the tag of each frontCard to match the view index to keep them aligned.
            frontOfCardView.tag = viewIndex;
            [cards addObject:frontOfCardView];

            if(viewIndex == 0)
              NSLog(@"first frontcard frame %@, first _viewContentsFrame %@", [cards objectAtIndex:0], [views objectAtIndex:0]);
    
        
        return cards;
    

我在上面添加了 UIButtons,上面有一个图像来表示“扑克牌的正面”。所述按钮的索引与 _viewContents 数组的索引完全匹配。我将每个按钮设置为与已经初始化的按钮的框架(大小和位置)匹配。

-(IBAction)frontOfCardPushed:(id)sender
int containerViewIndex = -1;
UIButton *pressedButton = (UIButton *)sender;
NSLog(@"Button Index: %i", pressedButton.tag);
containerViewIndex = pressedButton.tag;
UIView *container = [TileView viewWithTag:containerViewIndex];
UIView *viewToShow = [_viewContents objectAtIndex:containerViewIndex];
NSLog(@"container: %@, viewToShow: %@", container, viewToShow);
//viewToShow.frame = container.frame;
//NSLog(@"container: %@, viewToShow: %@", container, viewToShow);

[UIView transitionWithView:container duration:0.5 options:UIViewAnimationOptionTransitionFlipFromRight
                animations:^
                    [sender removeFromSuperview];
                    [container addSubview:viewToShow];
                
                completion:nil];

我想要做的是使用过渡 - 即“UIViewAnimationTransitionFlipFromRight”,在按下时“翻转卡片”以显示关联的 _viewContents 数组的内容。我还希望用户能够将卡片翻转过来以显示 frontCardButton 的图像。上面这段代码只关注卡片的第一次翻转,以避免出现代码墙。

我看到了合适的背景颜色 - 但动画本身不起作用。

我做错了什么,我需要在这段代码中添加/删除什么才能使其正常工作?

编辑* 如下 Moxy 所述,查看 getRequestedViewFrontCardImagesWithFrame 方法中的内容的输出。查看第一张卡片的输出,然后查看 frontCardPushed: 方法中容器视图的输出,然后使用适当的标签请求视图 - 它们不匹配。我正在打印按钮索引,以便您可以看到我正在点击第一个 TileView 子视图。 _viewContents 视图的标签是一个标签集,代表一种颜色,与我在 getRequestedViewFrontCardImagesWithFrame 方法中分配的 TileView 标签无关。然而,令人费解的是,当我打印出第一个按钮时,在我为第一个按钮分配一个标签之后,没有列出一个标签。

输出:

//why doesnt the first frontcard frame list a tag? I had just assigned it one?

第一个前卡帧 UIButton: 0x1c53f020;帧 = (6 5; 145 145); 不透明=否;层=CALayer:0x1c534ac0,第一个_viewContentsFrame UIButton:0x1d045220;帧 = (6 5; 145 145);不透明=否;标签 = 3; 层=CALayer:0x1d04535

按钮索引:0 容器:UIView:0x1c53a400;帧 = (51 83; 922 614); clipsToBounds = YES;自动调整大小 = W+H;手势识别器 = NSArray: 0x1d05afe0>;层 = CALayer:0x1c53a460,viewToShow: UIButton:0x1d045220;帧 = (6 5; 145 145);不透明=否;标签 = 3; 层=CALayer:0x1d045350

【问题讨论】:

-addSubview: 不可动画。尝试使用按钮状态,而不是使用其他视图。 developer.apple.com/library/ios/#documentation/uikit/reference/…: - 从苹果那里看看这个。(这可能无法让你正确使用 transitionWithView 方法)。我基本上在这里做同样的事情。引用此方法:以下代码为指定的容器视图创建翻转过渡......" 我的评论脱离了上下文,因为 +transitionWithView:duration:options:animations:completion: 动画容器视图的翻转而不是添加子视图的事实 【参考方案1】:

更简单的方法是使用 [UIView transitionWithView]:

[UIView transitionWithView:self.flipButton
                  duration:0.8
                   options:displayingFirstButton ? UIViewAnimationOptionTransitionFlipFromLeft : UIViewAnimationOptionTransitionFlipFromRight
                animations:^
                  //change image for button
                
                completion:^(BOOL finished) 

                ];

【讨论】:

【参考方案2】:

您可以通过使用容器视图来按住按钮使其工作。

-(void)viewDidAppear:(BOOL)animated

    _viewContents = [model getRequestedView];  //this retrieves an array of UIButtons(24 of them) whose frames and background colors have already been initialized to fit within the TileView.

    for(int i = 0; i < [_viewContents count]; i++)
        UIView *container = [[UIView alloc] initWithFrame:[[_viewContents objectAtIndex:screenViewIndex] frame]]; 
        container.tag = i;      
        UIButton *frontOfCardView = [[UIButton alloc] initWithFrame:container.bounds];
        frontOfCardView.backgroundColor = [UIColor colorWithPatternImage:_frontOfCardImage];
        frontOfCardView.tag = i;
        [container addSubview:frontOfCardView]; 
        [frontOfCardView addTarget:self action:@selector(frontOfCardPushed:) forControlEvents:UIControlEventTouchUpInside];
        [tileView addSubview:container];
    


-(IBAction)frontOfCardPushed:(UIButton *)sender

    UIView *container = [tileView viewWithTag:sender.tag];
    UIView *viewToShow = [_viewContents objectAtIndex:index];
    viewToShow.frame = sender.frame;
    [UIView transitionWithView:container
                      duration:0.5
                       options:UIViewAnimationOptionTransitionFlipFromRight
                    animations:^
                      [sender removeFromSuperView];
                      [container addSubview:viewToShow];
                    
                    completion:nil];

我还没有测试过,但它应该可以工作。无论如何,这不是很干净/漂亮的代码。但是,我知道这只是为了学习目的。

如果它仍然不起作用,请告诉我。

编辑:

我做了一个简单的项目,以单个视图控制器为例。我认为您跟踪所有视图/按钮的方式是无用的。在这种情况下,模型只是一种背景颜色,您要做的就是显示/隐藏它,因此一旦将颜色分配给按钮,它所知道的就是切换状态,因此您甚至不需要标签。(在其他情况下,控制器需要识别哪个按钮接收到事件,以便询问模型它需要什么内容)

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) NSArray *colors;
@property (nonatomic, strong) UIView *tileView;
@end

@implementation ViewController

#pragma mark -
#pragma Getters and setters

// I'm assuming the tileView is just a simple view to wrap all the buttons
// PS : propertys should always start with a lowercase letter
-(UIView *)tileView

    if (!_tileView) 
        _tileView = [[UIView alloc] initWithFrame:self.view.bounds];
        _tileView.backgroundColor = [UIColor greenColor];
    
    return _tileView;


// The model: just an array of colors, you can generate it or use other complicated stuff
-(NSArray *)colors

    if (!_colors) 
        _colors = @[[UIColor blackColor], [UIColor redColor], [UIColor grayColor], [UIColor yellowColor],
                    [UIColor whiteColor], [UIColor blueColor], [UIColor orangeColor], [UIColor darkGrayColor],
                    [UIColor lightGrayColor], [UIColor cyanColor], [UIColor brownColor], [UIColor magentaColor],
                    [UIColor blackColor], [UIColor redColor], [UIColor grayColor], [UIColor yellowColor],
                    [UIColor whiteColor], [UIColor blueColor], [UIColor orangeColor], [UIColor darkGrayColor],
                    [UIColor lightGrayColor], [UIColor cyanColor], [UIColor brownColor], [UIColor magentaColor]];
    
    return _colors;


#pragma mark -
#pragma mark Helper methods

#define ROWS 6
#define COLUMNS 4
#define CARD_HEIGHT 60.0f
#define CARD_WIDTH 40.0f

// Gets the width that each card can use based on the chose number of columns
-(CGFloat)horizontalStep

    return self.tileView.frame.size.width / COLUMNS;


// Gets the height that each card can use based on the chose number of columns
-(CGFloat)verticalStep

    return self.tileView.frame.size.height / ROWS;


// Calculates the center for the card frame (return (0,0) if the index is out of bounds)
-(CGPoint)centerForCardAtIndex:(NSUInteger)index

    CGFloat x = 0.0f, y = 0.0f;
    if (index < [self.colors count]) 
        x = [self horizontalStep] * 0.5f + (index % COLUMNS) * [self horizontalStep];
        y = [self verticalStep] * 0.5f + (index / COLUMNS) * [self verticalStep];
    
    return CGPointMake(x, y);


// Gets the frame for the card at index
-(CGRect)frameForCardAtIndex:(NSUInteger)index

    CGPoint center = [self centerForCardAtIndex:index];
    CGPoint origin = CGPointMake(center.x - CARD_WIDTH * 0.5f, center.y - CARD_HEIGHT * 0.5f);
    return CGRectMake(origin.x, origin.y, CARD_WIDTH, CARD_HEIGHT);


-(void)layoutCards

    // Add tileView to the controller's view
    [self.view addSubview:self.tileView];
    // Create the card buttons and add them to the tile view
    for (int i = 0; i < [self.colors count]; i++) 
        UIButton *card = [UIButton buttonWithType:UIButtonTypeCustom];
        card.frame = [self frameForCardAtIndex:i];

        // The only use of the model IN THIS CASE
        card.backgroundColor = self.colors[i];
        [card setImage:[UIImage imageNamed:@"back_of_tile.png"]
              forState:UIControlStateNormal];

        [card addTarget:self
                 action:@selector(cardPressed:)
       forControlEvents:UIControlEventTouchUpInside];

        [self.tileView addSubview:card];
    


-(void)cardPressed:(UIButton *)card

    [UIView transitionWithView:card
                      duration:0.5
                       options:UIViewAnimationOptionTransitionFlipFromRight
                    animations:^
                        card.selected = !card.selected;
                        if (!card.selected) 
                            [card setImage:[UIImage imageNamed:@"back_of_tile.png"]
                                  forState:UIControlStateNormal];
                         else 
                            [card setImage:nil
                                  forState:UIControlStateNormal];
                        
                    
                    completion:nil];



- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self layoutCards];

【讨论】:

好的,我将在今晚晚些时候进行测试,然后再回复您另一条评论 - 感谢您迄今为止的帮助。 Moxy - 我在上面编辑了我的代码并添加了一个代码 sn-p,这样您就可以更好地了解正在发生的事情。由于某种原因,容器 UIView 框架设置变得混乱。在我更新的代码中,您可以看到我正在谈论的内容的输出。除此之外,整个视图 TileView(它是在 IB 中设置的控制器视图的子视图)翻转,而不仅仅是我单击的子视图。奇怪的行为。让我知道你的想法 请注意我放在那里的新方法。您将看到我已经将框架分配给我已经从上一次调用中加载的 _viewcontents 框架。我提前这样做是为了确保frontCardButtons 已经与_viewContents UIButtons 对齐。请参考我的输出以查看当我按下第一个按钮(索引 0)时,该按钮的框架似乎很古怪并且在 x,y 原点偏移。这令人费解。如果您想进一步交谈,请告诉我,以便我们可以通过 IM 或其他方式交谈。再次感谢您的帮助。 Moxy - 感谢编辑的代码。这个应用程序的范围是一个匹配的卡片应用程序,它有许多屏幕,每个屏幕都有一组 24 个图块。瓷砖可以有除了背景颜色以外的东西。它们后面可以有图像,甚至可以自定义 Quartz 2D 图形。每次应用程序运行时,这些内容都会随机分布在多个屏幕上。 TileView 是主视图控制器视图的子视图,在 IB 中定义。现在我正在使用滑动手势在一组新的图块中设置动画,这些图块在该特定屏幕上可能不匹配。 我知道 Paul Hegarty 的任务。问题是关于过渡动画的......所以也许讨论整个项目会让这里变得混乱。如果您愿意,我们可以使用聊天进行讨论。

以上是关于按下时使用 UIViewAnimationTransitionFlipFromRight 翻转 UIButton 并在“另一侧”显示新按钮的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI,按下时如何更改视图(不使用按钮)颜色?

按下时如何获取 UIButton 的标题

在悬停并按下时更改 QPushButton 图标

使用 TouchableHighlight 在 React Native 中按下时如何更改按钮颜色?

android在使用选择器xml按下时更改ImageButton的图像

在外面按下时关闭对话框片段