简体   繁体   中英

Something other than snapToPoint is controlling layout in UISnapBehavior

I am new to animation and struggling to understand why UIAnimation and UISnapBehavior appear to shift UIButtons off-centre. Clearly there is something controlling layout besides what is in my code. Maybe someone can tell me what I'm missing.

Originally my ViewController had five UIButtons placed programmatically (ie without a XIB file) on the circumference of the circle as shown in Figure A. In revising my app I introduced snap animation to move UIButtons into position from a centre point. Figure B shows the centre point where animation starts. Figure C shows the five UIButtons which are displaced after animation finishes even though CGPoints used for snapToPoint: are exactly the same as CGPoints used to place UIButtons.

在此处输入图片说明

When I clicked in the black rectangular area at the bottom of the screen I noticed a message in the logged report unexpected nil window in _UIApplicationHandleEventFromQueueEvent which included the size properties of frame. This suggested that frames or bounds may be a reason for UIButtons being off centre following animation.

Here is the latest version of the code I used to demonstrate what happens. I believe it is correct even if it wouldn't win any prizes in an Object-Oriented beauty contest :-)

Firstly, the five buttons without animation shown in Figure A.

- (void)viewDidLoad {
NSLog(@"load GlobalView");

CGRect dot1 = (CGRect) { .origin.x = 38.0f,  .origin.y = 263.0f, .size.width = 80.0f, .size.height = 80.0f,};
CGRect dot2 = (CGRect) { .origin.x = 207.0f, .origin.y = 263.0f, .size.width = 80.0f, .size.height = 80.0f,};
CGRect dot3 = (CGRect) { .origin.x = 123.0f, .origin.y = 320.0f, .size.width = 80.0f, .size.height = 80.0f,};
CGRect dot4 = (CGRect) { .origin.x = 170.0f, .origin.y = 167.0f, .size.width = 80.0f, .size.height = 80.0f,};
CGRect dot5 = (CGRect) { .origin.x = 70.0f,  .origin.y = 167.0f, .size.width = 80.0f, .size.height = 80.0f,};

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

EDIT. The following lines of code redefine origin so CGRect is centred on the circumference of the circle. They replace the lines above which define origin as the top left apex of CGRect . Animation now places UIButtons according to the layout shown in Figure A.

 */

float width  = 80.0f;
float height = 80.0f;

CGRect dot1 = (CGRect) { .origin.x =  38.0f + width/2, .origin.y = 263.0f + width/2, width, height};
CGRect dot2 = (CGRect) { .origin.x = 207.0f + width/2, .origin.y = 263.0f + width/2, width, height};
CGRect dot3 = (CGRect) { .origin.x = 123.0f + width/2, .origin.y = 320.0f + width/2, width, height};
CGRect dot4 = (CGRect) { .origin.x = 170.0f + width/2, .origin.y = 167.0f + width/2, width, height};
CGRect dot5 = (CGRect) { .origin.x =  70.0f + width/2, .origin.y = 167.0f + width/2, width, height};

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// Insert global rings
UIView *ringView = [[UIView alloc] initWithFrame:CGRectMake(60.0f, 178.0f, 200.0f, 200.0f)];
UIImage *ringImage = [UIImage imageNamed:@"GlobalRings.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:ringImage];
imageView.frame = ringView.bounds;  // frame imageView in ringView and fill ringView
[ringView addSubview:imageView];    // add the imageview to the superview
[self.view addSubview:ringView];    //add the view to the main view

// place 5 UIButtons

UIButton *navigationButton1 = [UIButton buttonWithType:UIButtonTypeCustom];
[navigationButton1 setTitleColor:[UIColor blackColor] forState: UIControlStateNormal];
navigationButton1.frame = CGRectMake(38.0, 263.0, 80.0, 80.0);
[navigationButton1 setImage:[UIImage imageNamed:@"Dot1.png"] forState:UIControlStateNormal];
[navigationButton1 addTarget:self action:@selector(goToVenue1) forControlEvents:UIControlEventTouchUpInside];

UIButton *navigationButton2 = [UIButton buttonWithType:UIButtonTypeCustom];
[navigationButton2 setTitleColor:[UIColor blackColor] forState: UIControlStateNormal];
navigationButton2.frame = CGRectMake(207.0, 263.0, 80.0, 80.0);
[navigationButton2 setImage:[UIImage imageNamed:@"Dot2.png"] forState:UIControlStateNormal];
[navigationButton2 addTarget:self action:@selector(goToVenue2) forControlEvents:UIControlEventTouchUpInside];

UIButton *navigationButton3 = [UIButton buttonWithType:UIButtonTypeCustom];
[navigationButton3 setTitleColor:[UIColor blackColor] forState: UIControlStateNormal];
navigationButton3.frame = CGRectMake(123.0, 320.0, 80.0, 80.0);
[navigationButton3 setImage:[UIImage imageNamed:@"Dot3.png"] forState:UIControlStateNormal];
[navigationButton3 addTarget:self action:@selector(goToVenue3) forControlEvents:UIControlEventTouchUpInside];

UIButton *navigationButton4 = [UIButton buttonWithType:UIButtonTypeCustom];
[navigationButton4 setTitleColor:[UIColor blackColor] forState: UIControlStateNormal];
navigationButton4.frame = CGRectMake(170.0, 167.0, 80.0, 80.0);
[navigationButton4 setImage:[UIImage imageNamed:@"Dot4.png"] forState:UIControlStateNormal];
[navigationButton4 addTarget:self action:@selector(goToVenue4) forControlEvents:UIControlEventTouchUpInside];

UIButton *navigationButton5 = [UIButton buttonWithType:UIButtonTypeCustom];
[navigationButton5 setTitleColor:[UIColor blackColor] forState: UIControlStateNormal];
navigationButton5.frame = CGRectMake(70.0, 167.0, 80.0, 80.0);
[navigationButton5 setImage:[UIImage imageNamed:@"Dot5.png"] forState:UIControlStateNormal];
[navigationButton5 addTarget:self action:@selector(goToVenue5) forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:navigationButton1];
[self.view addSubview:navigationButton2];
[self.view addSubview:navigationButton3];
[self.view addSubview:navigationButton4];
[self.view addSubview:navigationButton5];

[super viewDidLoad];
}

Then to place the five UIButtons at the centre before the animation starts I aligned each UIButton with the frame centre by adding

CGRect preSnap = (CGRect) { .origin.x = 120.0f, .origin.y = 240.0f, .size.width = 80.0f, .size.height = 80.0f,};

and then changing each button frame (below) to set the starting point for the animation

navigationButton1.frame = preSnap;
navigationButton2.frame = preSnap;
navigationButton3.frame = preSnap;
navigationButton4.frame = preSnap;
navigationButton5.frame = preSnap;

Then I added UISnapBehaviour to the animation by inserting pointers immediately after viewDidLoad

UIDynamicAnimator* _animator1;
UIDynamicAnimator* _animator2;
UIDynamicAnimator* _animator3;
UIDynamicAnimator* _animator4;
UIDynamicAnimator* _animator5;
UISnapBehavior* _snap1;
UISnapBehavior* _snap2;
UISnapBehavior* _snap3;
UISnapBehavior* _snap4;
UISnapBehavior* _snap5;

and adding UIDynamicAnimator and UISnapBehavior methods just before [super viewDidLoad]; resulting in the UIButton layout shown in Figure C

_animator1 = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_snap1  = [[UISnapBehavior alloc] initWithItem:navigationButton1 snapToPoint: dot1.origin];
[_animator1 addBehavior:_snap1];

_animator2 = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_snap2  = [[UISnapBehavior alloc] initWithItem:navigationButton2 snapToPoint: dot2.origin];
[_animator2 addBehavior:_snap2];

_animator3 = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_snap3  = [[UISnapBehavior alloc] initWithItem:navigationButton3 snapToPoint: dot3.origin];
[_animator3 addBehavior:_snap3];

_animator4 = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_snap4  = [[UISnapBehavior alloc] initWithItem:navigationButton4 snapToPoint: dot4.origin];

[_animator4 addBehavior:_snap4];

_animator5 = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_snap5  = [[UISnapBehavior alloc] initWithItem:navigationButton5 snapToPoint: dot5.origin];
[_animator5 addBehavior:_snap5];

[super viewDidLoad];

}

Finally I added five methods to make sure all buttons worked

- (void)goToVenue1 {
NSLog(@"Button 1 worked");
}
- (void)goToVenue2 {
NSLog(@"Button 2 worked");
}
- (void)goToVenue3 {
NSLog(@"Button 3 worked");
}
- (void)goToVenue4 {
NSLog(@"Button 4 worked");
}
- (void)goToVenue5 {
NSLog(@"Button 5 worked");
}

The problem is solved. In earlier drafts the origin coordinates were those on the top left apex. It was necessary to redefine origin so CGRect is centred on the circumference of the circle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM