UILabel stops animations - objective-c

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.

Related

Adding UISearchBar in UINavigationBar with constraints

All,
I want to add UISearchBar to UINavigationbar, I dont dont want to use UISearchController, Just UISearchbar programmatically and it must work in landscape as well.
I tried it is working well in Portrait well, but in Landscape, i have issues in iPhone X width. Can we use Constraints.
Below is the code
CGFloat width = [UIScreen mainScreen].bounds.size.width;
search = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, width - 2 * 44 - 2 * 15, 44)];
search.delegate = self; // search.tintColor = [UIColor redColor];
search.searchBarStyle = UISearchBarStyleMinimal;
search.placeholder = #"Search";
search.translucent = NO;
search.opaque = NO;
search.showsCancelButton = NO;
[search setBackgroundImage:[[UIImage alloc] init]];
//customize textfield inside UISearchBar
#try {
for (id object in [[[search subviews] firstObject] subviews])
{
if (object && [object isKindOfClass:[UITextField class]])
{
UITextField *textFieldObject = (UITextField *)object;
textFieldObject.backgroundColor = [UIColor whiteColor];
textFieldObject.borderStyle = UITextBorderStyleRoundedRect;
textFieldObject.layer.borderColor = (__bridge CGColorRef _Nullable)([brandingObj getValueForKey:navBarTitleColor]);
textFieldObject.layer.borderWidth = 1.0;
break;
}
}
}
#catch (NSException *exception) {
NSLog(#"Error while customizing UISearchBar");
}
#finally {
}
I don't understand why you want to use UISearchDisplayController, its highly configurable and recommended by apple, working with geometry (CGRecr, CGFrame, etc.) to adjust UIKit objects layout could be a pain, use auto layout avoiding UIKits convenience initializers, to adjust later constraints.
Anyway if you want explicitly do with this way, this should work.
#import "ViewController.h"
#interface ViewController ()<UISearchBarDelegate>
#property UISearchBar *searchbar;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_searchbar = [UISearchBar new];
_searchbar.delegate = self;
_searchbar.searchBarStyle = UISearchBarStyleMinimal;
self.navigationItem.titleView = self.searchbar;
// Do any additional setup after loading the view, typically from a nib.
}
Maybe you should need configure appearance , take a look here
Cheers.

Find where UIimageview frame is being set

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.

creating a line between two UIlabels using the long press gesture recognizer

i am developing a ER diagram editor, i have a bunch of draggable UILabels but all of them have the same name. i want to be able to create a line between two UIlabels when both are pressed together using the long press gesture recognizer. any help will be most appreciated
You can create your long press gesture on the superview shared by these two labels, e.g.:
UILongPressGestureRecognizer *twoTouchLongPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(handleLongPress:)];
twoTouchLongPress.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:twoTouchLongPress];
You can then write a gesture handler:
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan)
{
CGPoint location0 = [gesture locationOfTouch:0 inView:gesture.view];
CGPoint location1 = [gesture locationOfTouch:1 inView:gesture.view];
if ((CGRectContainsPoint(self.label0.frame, location0) && CGRectContainsPoint(self.label1.frame, location1)) ||
(CGRectContainsPoint(self.label1.frame, location0) && CGRectContainsPoint(self.label0.frame, location1)))
{
NSLog(#"success; draw your line");
}
else
{
NSLog(#"failure; don't draw your line");
}
}
}
In the updated comments, you suggest that you're creating a local UILabel variable, and then adding the resulting label to the view. That's fine, but you really want to maintain a backing model, that captures what you're doing in the view. For simplicity's sake, let me assume that you'll have array of these labels, e.g.:
#property (nonatomic, strong) NSMutableArray *labels;
Which you then initialize at some point (e.g. viewDidLoad):
self.labels = [[NSMutableArray alloc] init];
Then as you add labels to your view, add a reference to them in your array:
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(xVal, yVal, 200.0f, 60.0f)];
label.text = sentence;
label.layer.borderColor = [UIColor blueColor].CGColor;
label.layer.borderWidth = 0.0;
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont systemFontOfSize:19.0f];
[self.view addSubview:label];
[self.labels addObject:label];
Then, your gesture can do something like:
- (UILabel *)labelForLocation:(CGPoint)location
{
for (UILabel *label in self.labels)
{
if (CGRectContainsPoint(label.frame, location))
return label; // if found one, return that `UILabel`
}
return nil; // if not, return nil
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan)
{
CGPoint location0 = [gesture locationOfTouch:0 inView:gesture.view];
CGPoint location1 = [gesture locationOfTouch:1 inView:gesture.view];
UILabel *label0 = [self labelForLocation:location0];
UILabel *label1 = [self labelForLocation:location1];
if (label0 != nil && label1 != nil && label0 != label1)
{
NSLog(#"success; draw your line");
}
else
{
NSLog(#"failure; don't draw your line");
}
}
}
Frankly, I'd rather see this backed by a proper model, but that's a more complicated conversation beyond the scope of a simple Stack Overflow answer. But hopefully the above gives you an idea of what it might look like. (BTW, I just typed in the above without assistance of Xcode, so I'll apologize in advance for typos.)

Does it make sense to add ATMHud to tabBarController.view

When i try to add ATMHud to uitableviewcontroller subview it does work but it doesn't disable the scrolling and if the tableview is not on top i can't see the hud view. What i did was added to teh tabBarController.view that works but i want find out if this is a good idea or later on i might have issues with it.
Another question is tabBarController.view frame is that the whole screen or just the bottom part. How come atmhud shows in the middle of the screen?
Thanks in advance!
Yan
============
Found a blog post that shows how to reset self.view and add tableview separately in uitableviewcontroller
UITableViewController and fixed sub views
- (void)viewDidLoad {
[super viewDidLoad];
if (!tableView &&
[self.view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)self.view;
}
self.view = [[[UIView alloc] initWithFrame:
[UIScreen mainScreen].applicationFrame] autorelease];
self.tableView.frame = self.view.bounds;
self.tableView.contentInset = UIEdgeInsetsMake(44.0, 0.0, 0.0, 0.0);
[self.view addSubview:self.tableView];
UIView *fixedBar = [[UIView alloc] initWithFrame:
CGRectMake(0.0, 0.0, self.view.bounds.size.width, 44.0)];
fixedBar.backgroundColor = [UIColor colorWithRed:
0.0 green:1.0 blue:0.0 alpha:0.7];
[self.view addSubview:fixedBar];
[fixedBar release];
}
After this when add hud to self.view you will be able to disable the tableview on the bottom.
Let me know if this a good way to setup the tableview
The problem with using the tab bar is that the hud is now modal, and the user cannot change the tab.
It sounds like the tableview is not your primary viewm, as it can get "covered up". If its not the primary view, then add the ATMHud to self.view. If the tableView is the same as self.view, then add a new transparent view to it, then add the HUD to that view.
The tabBarController.view is the view that hosts the tabbed views - if you want to see its size (or frame) log it using NSStringFromCGRect(self.tabBarController.frame);
EDIT: I just did a test, the ATMHud DOES block the UI. All I can think of is that you have not inserted it where you need to (at the top of current view's subviews.) I have a demo project where I do this:
hud = [[ATMHud alloc] initWithDelegate:self];
[self.view addSubview:hud.view];
[hud setCaption:#"Howdie"];
[hud setActivity:YES];
[hud show];
[hud hideAfter:5];
A button under the hud is not active - in fact nothing in the view is active (probably the Nav Bar would be live though)
If you want an ARCified and field tested version, you can grab it here
EDIT2: The solution to your problem is below. Note that ATMHud blocks clicks from getting to the table, and the code below stops the scrolling:
- (void)hudWillAppear:(ATMHud *)_hud
{
self.tableView.scrollEnabled = NO;
}
- (void)hudDidDisappear:(ATMHud *)_hud
{
self.tableView.scrollEnabled = YES;
}
Dump the views:
#import <QuartzCore/QuartzCore.h>
#import "UIView+Utilities.h"
#interface UIView (Utilities_Private)
+ (void)appendView:(UIView *)v toStr:(NSMutableString *)str;
#end
#implementation UIView (Utilities_Private)
+ (void)appendView:(UIView *)a toStr:(NSMutableString *)str
{
[str appendFormat:#" %#: frame=%# bounds=%# layerFrame=%# tag=%d userInteraction=%d alpha=%f hidden=%d\n",
NSStringFromClass([a class]),
NSStringFromCGRect(a.frame),
NSStringFromCGRect(a.bounds),
NSStringFromCGRect(a.layer.frame),
a.tag,
a.userInteractionEnabled,
a.alpha,
a.isHidden
];
}
#end
#implementation UIView (Utilities)
+ (void)dumpSuperviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
while(v) {
[self appendView:v toStr:str];
v = v.superview;
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
+ (void)dumpSubviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
if(v) [self appendView:v toStr:str];
for(UIView *a in v.subviews) {
[self appendView:a toStr:str];
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
#end

UIPageControl doesn't properly react to taps

In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.