I am working on an iPad application in Xcode 5 for a jigsaw puzzle game. I created the layout in storyboard, adding uiimageviews for each of 9 frames that would hold part of the image that was going to be the puzzle, and placed them on the view controller. Someone else has since removed the storyboard from the game and I need to go back and finish up the puzzle without it. The biggest issue I have is that the position of the frames seem to be being set somewhere outside of the view controller.h or .m classes and I can't find it. I am able to change the position of the pieces using image1.center in the touchesmoved method, but not in the viewdidload. I have been trying to track down where this is being set for several hours and am having no luck. Setting any movement of the center in viewdidload,viewwillappear, viewdidappear etc do not change the position as expected.
Can anyone either guide me on a way to change the image1.center after the view loads but without any user input, or else tell me how to trace all changes that are occurring in the view? I don't think seeing my code will help in this question since it is not seemingly changed in the classes that I am looking at, but I will post what works and what doesn't. I am only posting the portions that touch the uiimageview (1-9)
from .h
#implementation PuzzleViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
#interface PuzzleViewController : UIViewController
{
UIImageView *image1;
UIImageView *image2;
UIImageView *image3;
UIImageView *image4;
UIImageView *image5;
UIImageView *image6;
UIImageView *image7;
UIImageView *image8;
UIImageView *image9;
UIImage *puzzlePic;
}
from .m
- (void)viewDidLoad
{
[super viewDidLoad];
//set up buttons to accept user input
image1.userInteractionEnabled = true;
image2.userInteractionEnabled = true;
image3.userInteractionEnabled = true;
image4.userInteractionEnabled = true;
image5.userInteractionEnabled = true;
image6.userInteractionEnabled = true;
image7.userInteractionEnabled = true;
image8.userInteractionEnabled = true;
image9.userInteractionEnabled = true;
// Do any additional setup after loading the view from its nib.
puzzlePic = [LLFileManager getImage:#"star7.gif" folder:#"animation_images"];
[self placeImage];
}
// handle touches within images to move
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if([touch view] == image1){
image1.center = touchLocation;
}
else if([touch view] == image2){
image2.center = touchLocation;
}
else if([touch view] == image3){
image3.center = touchLocation;
}
else if([touch view] == image4){
image4.center = touchLocation;
}
else if([touch view] == image5){
image5.center = touchLocation;
}
else if([touch view] == image6){
image6.center = touchLocation;
}
else if([touch view] == image7){
image7.center = touchLocation;
}
else if([touch view] == image8){
image8.center = touchLocation;
}
else if([touch view] == image9){
image9.center = touchLocation;
}
}
// splits image into 9 equal parts, takes input of a ui image
-(NSArray *)splitImage9:(UIImage *)im{
NSMutableArray *images = [NSMutableArray array];
CGSize imageSize = im.size;
CGFloat xPos = 0.0, yPos = 0.0;
CGFloat width = imageSize.width/3;
CGFloat height = imageSize.height/3;
for (int y = 0; y < 3; y++) {
xPos = 0.0;
for (int x = 0; x < 3; x++) {
CGRect rect = CGRectMake(xPos, yPos, width, height);
CGImageRef cImage = CGImageCreateWithImageInRect([im CGImage], rect);
UIImage *dImage = [[UIImage alloc] initWithCGImage:cImage];
[images addObject:dImage];
xPos += width;
}
yPos += height;
}
return images;
}
// position pieces on board load
-(void)positionPieces{ // test, not working
CGPoint piece = CGPointMake(50,100);
image2.center = piece;
}
// places the cut images into view placeholders
-(void)placeImage{
NSArray *puzzleImage = [self splitImage9: puzzlePic];
[image1 setImage:[puzzleImage objectAtIndex:0]];
[image2 setImage:[puzzleImage objectAtIndex:1]];
[image3 setImage:[puzzleImage objectAtIndex:2]];
[image4 setImage:[puzzleImage objectAtIndex:3]];
[image5 setImage:[puzzleImage objectAtIndex:4]];
[image6 setImage:[puzzleImage objectAtIndex:5]];
[image7 setImage:[puzzleImage objectAtIndex:6]];
[image8 setImage:[puzzleImage objectAtIndex:7]];
[image9 setImage:[puzzleImage objectAtIndex:8]];
}
The biggest issue I have is that the position of the frames seem to be being set somewhere outside of the view controller.h or .m classes and I can't find it.
If the storyboard is gone, and if no code has been added to put the image views into the interface, then the interface should be empty. The reason you are seeing the interface at all must be because you are accidentally running an outdated version of the project.
To solve that problem, clean the project as I explain here:
How to Empty Caches and Clean All Targets Xcode 4
You will then find, when you run the project again, that the interface is now empty and the mystery is solved: the frames have no position, because there are no frames, because there are no image views at all.
Related
I am using the following code to scroll to the next page of a UITableView that has a scrollview. Scrolling works but the page the user lands to depends on the power he scrolls with his finger. Whatever the force of scrolling is it needs to land to the next page. How can i achieve this? Any help appreciated...
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint
*)targetContentOffset {
self.tableView.rowHeight = [UIScreen mainScreen].bounds.size.height-self.navigationController.navigationBar.frame.size.height-(self.appDelegate.heightOfEightFeedWalletTopConstraint*2)+12;
int tomove = ((int)targetContentOffset->y % (int)self.tableView.rowHeight);
if(tomove < self.tableView.rowHeight/6 || velocity.y < 0){
targetContentOffset->y -= tomove;
}
else{
targetContentOffset->y += (self.tableView.rowHeight-tomove);
}
}
You don't need any of that code...
All you need to do is enable paging on the scroll view.
This will
create a scroll view with 20-points on all 4 sides
add a vertical stack view
add 10 labels as arranged subviews (each label will be the height of the scroll view frame)
Scroll view has paging enabled:
#import <UIKit/UIKit.h>
#interface PageScrollViewController : UIViewController
{
UIScrollView *scrollView;
}
#end
#implementation PageScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
scrollView = [UIScrollView new];
[scrollView setPagingEnabled:YES];
UIStackView *stack = [UIStackView new];
stack.axis = UILayoutConstraintAxisVertical;
stack.distribution = UIStackViewDistributionFillEqually;
stack.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:stack];
[self.view addSubview:scrollView];
// respect safeArea
UILayoutGuide *g = self.view.safeAreaLayoutGuide;
UILayoutGuide *cg = scrollView.contentLayoutGuide;
[NSLayoutConstraint activateConstraints:#[
[scrollView.topAnchor constraintEqualToAnchor:g.topAnchor constant:20.0],
[scrollView.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:20.0],
[scrollView.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-20.0],
[scrollView.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:-20.0],
[stack.topAnchor constraintEqualToAnchor:cg.topAnchor constant:0.0],
[stack.leadingAnchor constraintEqualToAnchor:cg.leadingAnchor constant:0.0],
[stack.trailingAnchor constraintEqualToAnchor:cg.trailingAnchor constant:0.0],
[stack.bottomAnchor constraintEqualToAnchor:cg.bottomAnchor constant:0.0],
[stack.widthAnchor constraintEqualToAnchor:scrollView.frameLayoutGuide.widthAnchor],
]];
// now let's add some views to the stack view
// making each one the same height as the scroll view frame
NSArray <UIColor *> *colors = #[
UIColor.yellowColor,
UIColor.greenColor,
UIColor.cyanColor,
UIColor.orangeColor,
];
for (int i = 0; i < 10; i++) {
UILabel *v = [UILabel new];
v.textAlignment = NSTextAlignmentCenter;
v.text = [NSString stringWithFormat:#"Label %d", i];
v.backgroundColor = colors[i % colors.count];
[stack addArrangedSubview:v];
[v.heightAnchor constraintEqualToAnchor:scrollView.frameLayoutGuide.heightAnchor].active = YES;
}
}
#end
I have 5 UIImageViews getting animated down the screen. If one is pressed, and it meets the requirements, it will add 1 to your score, using this:
self.score.text = #(self.score.text.integerValue + 1).stringValue;
But when the text in the UILabel updates, all the animations stop abruptly and the remaining UIImageViews disappear. But, the animation only restarts after a few seconds, as if the images are becoming hidden. The code to animate the images and change image(They are the same for each one):
- (void) squareOneMover {
NSUInteger r = arc4random_uniform(3);
[self.squareOne setHidden:NO];
CGPoint originalPosition = self.squareOne.center;
CGPoint position = self.squareOne.center;
originalPosition.y = -55;
position.y += 790;
[self.squareOne setCenter:originalPosition];
[UIView animateWithDuration:r + 3
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
[self.squareOne setCenter:position];
}
completion:^(BOOL complete) {
if (complete) {
[self squareOneColour];
[self squareOneMover];
}
}
];
}
- (void) squareOneColour {
NSUInteger r = arc4random_uniform(5);
[self.squareOne setImage:[self.colorArray objectAtIndex:r]];
}
Anyone have a solution? And if by changing the text in a UILabel is supposed to stop animations (I don't know why they would do so) can someone provide a workaround to make keeping score possible.
Edit: I created a button that would increase the integer value of score. This means I can change the text in the UILabel manually. The animations still stopped the moment the text changed.
Edit 2: Method for the pressed event:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
NSUInteger a = [self.colorArray indexOfObject:self.squareOne.image];
NSUInteger b = [self.iconColorArray indexOfObject:self.icon.image];
if ([self.squareOne.layer.presentationLayer hitTest:touchLocation]) {
_squareOne.hidden = YES;
}
if (a!=b) {
if (self.squareOne.hidden==YES) {
NSLog(#"NO");
}
}
if (a==b) {
if ([self.squareOne.layer.presentationLayer hitTest:touchLocation]) {
self.score.text = #(self.score.text.integerValue + 1).stringValue;
}
}
if ([self.squareTwo.layer.presentationLayer hitTest:touchLocation]) {
_squareTwo.hidden = YES;
}
}
Looking at Matt's answer, how would I animate the UIImageView by "changing its constraints"?
Constraints:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-25-[btn1]-25-[btn2(==btn1)]-25-[btn3(==btn1)]-25-[btn4(==btn1)]-25-[btn5(==btn1)]-25-|" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:self.squareOne
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:0.0
constant:-55.0]];
The problem is this line:
[self.squareOne setCenter:position];
You are animating the position of squareOne by setting its position. But meanwhile you also have constraints that also position squareOne. That fact remains concealed until you change the text of the label; that triggers layout, and now the constraints all assert themselves, putting an end to everything else that was going on.
One solution is to animate the position of squareOne by changing its constraints. Now when layout is triggered, the existing constraints will match the situation because they are the only force that is positioning things.
//what all are you checking with the if's?
//I'm just curious there's a lot going on here
**//if this evaluates true
if ([self.squareOne.layer.presentationLayer hitTest:touchLocation]) {
_squareOne.hidden = YES;
}**
if (a!=b) {
if (self.squareOne.hidden==YES) {
NSLog(#"NO");
}
}
if (a==b) {
** //this will evaluate true also
if ([self.squareOne.layer.presentationLayer hitTest:touchLocation]) {
self.score.text = #(self.score.text.integerValue + 1).stringValue;
}**
}
//this works fine even when I added everything from interface builder which almost never happens
Update
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic)UILabel *label;
#property int tapCount;
#end
#implementation ViewController
-(void)viewDidLoad{
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
[tap addTarget:self action:#selector(userTap:)];
[self.view addGestureRecognizer:tap];
}
-(UILabel*)label{
if (!_label) {
_label = [[UILabel alloc]init];
_label.text = #"taps:%3i",0;
[_label sizeToFit];
_label.center = self.view.center;
[self.view addSubview:_label];
}
return _label;
}
-(void)userTap:(UITapGestureRecognizer*)sender {
NSLog(#"tap");
self.tapCount ++;
[UIView animateWithDuration:1
animations:^{
self.label.text = [NSString stringWithFormat:#"taps:%i",self.tapCount];
self.label.center = [sender locationInView:self.view];
}
completion:nil];
}
#end
**just cleaning up the code a bit for future onlookers **
direct copy and paste only delete everything inside ViewController.m
on a new project and paste this in it's place
Instead of animating the constraints by hand, you can also have UIImageView generate constraints during animation (which does not get broken by a sibling UILabel). Like in this question.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * t =[touches anyobject];
CGpoint * point = [t locationinview:self.view];
If([t view]) == imageview {
Imageview.center = point;
}
}
Check is point inside the image rectangle:
if (CGRectContainsPoint(imageview.bounds, point)) {
//point inside imageView frame
}
You can also set up gesture recogniser on that image view and required method will be called just user touch that specific image, for example:
-(void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTouched:)];
gr.numberOfTapsRequired = 1;
gr.numberOfTouchesRequired = 1;
[imageview addGestureRecognizer:gr];
}
-(void)imageTouched:(UITapGestureRecognizer*)recognizer{
//image view touched
}
I am trying to develop a game with multiple stages using cocos2d. In a level for example, I have 4 sprites, 2 white and 2 black. If the player hits the black sprites, the game is over, if he hits the white sprite, he wins. How can I implement a condition wherein if the player hits the white sprite, it checks if there are other white sprites present on the scene, if there is, the game continues. If there's none, then he goes to the stage clear scene? I tried putting the sprites in two different arrays (arrayBlack and arrayWhite) but I'm stuck with how I'm gonna make the condition for the white sprites. Can anybody please give me an idea or suggestion or a tutorial that shows a good example for this?
UPDATE:
I kind of figured it out myself. Here's my code:
-(id) init
{
if( (self=[super init]) ) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
self.isTouchEnabled = YES;
//These are declared in the .h class
blackArray = [[NSMutableArray alloc]init];
whiteArray = [[NSMutableArray alloc]init];
black1 = [CCSprite spriteWithFile:#"b1.png"];
black1.position = ccp(100, 160);
black2 = [CCSprite spriteWithFile:#"b2.png"];
black2.position = ccp(105, 150);
white = [CCSprite spriteWithFile:#"w1.png"];
white.position = ccp(150, 150);
white2 = [CCSprite spriteWithFile:#"w2.png"];
white2.position = ccp(80, 160);
[self addChild:black1 z:1 tag:1];
[self addChild:black2 z:1 tag:2];
[self addChild:white z:1 tag:3];
[self addChild:white2 z:1 tag:4];
[blackArray addObject:black1];
[blackArray addObject:black2];
[whiteArray addObject:white];
[whiteArray addObject:white2];
}
return self;}
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouch = [event allTouches];
UITouch *touch = [[allTouch allObjects]objectAtIndex:0];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
endTouch = location;
posX = endTouch.x;
//Minimum swipe length
posY = ccpDistance(beginTouch, endTouch);
//selectedSprite is a sprite declared in .h file
if(selectedSprite.tag == 1 || selectedSprite.tag == 2)
{
//action here
}
if([whiteArray count] > 0)
{
if(selectedSprite.tag == 3 || selectedSprite.tag == 4)
{
//action here
}
[whiteArray removeObject:selectedSprite];
if([whiteArray count] == 0)
{
//Go to game over
}
}}
This doesn't look pretty but it works. Anyway, if there's a better way to implement this than how I am currently doing it please let me know.
Make your arrays (arrayBlack and arrayWhite) mutable. Then,
if(user hit sprite1)
{
if([arrayBlack containsObject:sprite1])
{
[arrayBlack removeObject:sprite1];
// Game over
}
else
{
[arrayWhite removeObject:sprite1];
if(arrayWhite.count>0)
{
// Continue game
}
else
{
// Stage clear scene
}
}
}
There are a lot of questions with zooming in UIWebView, but I could not find an answer to mine.
I've got ViewController with WebView. I load an SVG-file into this WebView, and I want to zoom in by tapping on it. Here is the code:
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>
{
IBOutlet MyWebView *webView;
UITapGestureRecognizer *doubleTapRecognizer;
CGFloat k;
}
...
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:#"map" withExtension:#"svg"]]];
webView.scalesPageToFit = YES;
webView.scrollView.maximumZoomScale = 512;
webView.scrollView.minimumZoomScale = 0.1;
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTap:)];
doubleTapRecognizer.delegate = self;
doubleTapRecognizer.enabled = YES;
[webView addGestureRecognizer:doubleTapRecognizer];
k = 1;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (void)doubleTap:(UITapGestureRecognizer*)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
k = 10;
// No one does not work correctly
[webView.scrollView setZoomScale:k animated:YES];
// [webView setContentScaleFactor:k];
// [webView.scrollView setContentScaleFactor:k];
// webView.scrollView.zoomScale = k;
// [webView.scrollView zoomToRect:CGRectMake(webView.bounds.origin.x, webView.bounds.origin.y, webView.bounds.size.width/k, webView.bounds.size.height/k) animated:YES];
}
}
Now WebView is zooming in by the tap, but only x2 and no more. What is wrong and how could I zoom it x10?
Have you tried pinching ?
It's not because you've set the max zoom to be 512 or 10 that tapping twice will zoom to that level, what it means is tapping onto a small element in the web view, this will zoom in to fill the available space of the web view, regardless of the zoom value, up until the max zoom value.
So something appearing to occupy half the screen will make the zoom be x2, smaller would be more.