UIButton suddenly stops working - ios

So I'm having issues with the buttons for the filters in my photo app. I'm not exactly sure why, but they've suddenly stopped working. The selector is not being called when they're tapped, but I have no idea why. They were working just fine a few days ago, but for some reason they're not now. I've checked the UIView subviews array, verified that it's right on top. I need a way to see if, for some reason, the touches are not making it to the button. I'm not sure how to do this.
I wish I had more information to give, but this is all I've got. I hope someone has some suggestions because I'm at wit's end with it.
Thanks in advance!
Button Creation Method:
-(void)createFilterButtonsForThumbnail:(UIImage *)thumbnail
{
UIView *filtersContainer = self.filterContainer;
CGRect buttonFrame = CGRectMake(kFilterFrameThickness, kFilterFrameThickness,
thumbnail.size.width, thumbnail.size.height);
CGFloat frameWidth = thumbnail.size.width+(2*kFilterFrameThickness);
CGFloat frameHeight = kFilterPickerHeight;
UIEdgeInsets backgroundInsets = UIEdgeInsetsMake(0, kFilterFrameThickness, 0, kFilterFrameThickness);
UIImage *buttonBackgroundImage = [[UIImage imageNamed:#"FilmReel"] resizableImageWithCapInsets:backgroundInsets];
for (int i = 0;i<(self.filterPaths.count+kFilterNonLookups+1);i++){
UIImageView *buttonBackground = [[UIImageView alloc] initWithFrame:CGRectMake(kFilterSidePadding+(i*(frameWidth+kFilterSidePadding)),
0,
frameWidth,
frameHeight)];
[buttonBackground setImage:buttonBackgroundImage];
UIButton *thumbnailButton = [[UIButton alloc] initWithFrame:buttonFrame];
UIImage *filteredThumbnail = [self applyFilterAtIndex:i ToImage:thumbnail];
[thumbnailButton setImage:filteredThumbnail forState:UIControlStateNormal];
[thumbnailButton addTarget:self action:#selector(filterSelected:) forControlEvents:UIControlEventTouchUpInside];
[thumbnailButton setTag:i];
[buttonBackground addSubview:thumbnailButton];
[filtersContainer addSubview:buttonBackground];
if ((i > (kFilterProMinimumIndex)) && ([self isProVersion]) == NO){
UIImageView *proTag = [[UIImageView alloc] initWithImage:nil];
CGRect proFrame = CGRectMake(buttonFrame.origin.x,
buttonFrame.origin.y + buttonFrame.size.height - kFilterProIconHeight-kFilterFrameThickness,
kFilterProIconWidth,
kFilterProIconHeight);
[proTag setBackgroundColor:[UIColor orangeColor]];
[proTag setFrame:proFrame];
[thumbnailButton addSubview:proTag];
[self.filterProTags addObject:proTag];
}
}
}
Selector Method:
-(void)filterSelected:(UIButton *)button
{
NSLog(#"Pressed button for index %i",button.tag);
int buttonTag = button.tag;
if ((buttonTag < kFilterProMinimumIndex+1) || ([self isProVersion] == YES)){
[self.imageView setImage:[self applyFilterAtIndex:buttonTag ToImage:self.workingImage]];
}
else {
[self processProPurchaseAfterAlert];
}
}

I see a couple issues in this, but I'm not sure which ones are causing it to break. First, you should instantiate your button using buttonWithType: on UIButton:
UIButton *thumbnailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[thumbnailButton setFrame:buttonFrame]
The reason for this is UIButton is a class cluster, and you should let the OS give you the correct button.
Secondly, you're adding the Button as a Subview of an UIImageView, which by default, has userInteractionEnabled set to NO.
This property is inherited from the UIView parent class. This class
changes the default value of this property to NO.
You should have the button be one large button, with the background of the button be the image you want it to be.

Related

Wrong barButton item tint for play/pause buttons built programatically

I have a UIToolbar with audio controls. Sometime around iOS 7 upgrade the color of the bar changed and iOS started shading my buttons differently. That introduced a bug where the play and pause buttons which I change programatically don't match the new look of the toolbar:
Toolbar starts out looking fine:
Now I pressed play so code inserted pause button but wrong color:
Now I pressed pause and code inserted play button, again with wrong color:
I want the play and pause buttons to have that same dark look as the other buttons. I assume I have to do something differently when building the replacement buttons. The raw icon images are all that lighter color, but iOS toolbar seems to be automatically recoloring to darker color from the storyboard, as seen in first screenshot.
Here's the code I use to build the buttons:
- (UIBarButtonItem *) makeBarButton: (NSString *) imageName action:(SEL)targetAction
{
UIImage* image = [UIImage imageNamed:imageName];
CGRect frame = CGRectMake(0 - 20, 0 - 20, image.size.width + 20, image.size.height + 20);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:targetAction forControlEvents:UIControlEventTouchUpInside];
[button setImage:image forState:UIControlStateNormal];
[button setShowsTouchWhenHighlighted:YES];
return [[UIBarButtonItem alloc] initWithCustomView:button];
}
- (void) setAsPlaying:(BOOL)isPlaying
{
self.rootViewController.playing = isPlaying;
// we need to change which of play/pause buttons are showing, if the one to
// reverse current action isn't showing
if ((isPlaying && !self.pauseButton) || (!isPlaying && !self.playButton))
{
UIBarButtonItem *buttonToRemove = nil;
UIBarButtonItem *buttonToAdd = nil;
if (isPlaying)
{
buttonToRemove = self.playButton;
self.playButton = nil;
self.pauseButton = [self makeBarButton:#"icon_pause.png" action:#selector(pauseAudio:)];
buttonToAdd = self.pauseButton;
}
else
{
buttonToRemove = self.pauseButton;
self.pauseButton = nil;
self.playButton = [self makeBarButton:#"icon_play.png" action:#selector(playAudio:)];
buttonToAdd = self.playButton;
}
// Get the reference to the current toolbar buttons
NSMutableArray *toolbarButtons = [[self.toolbar items] mutableCopy];
// Remove a button from the toolbar and add the other one
if (buttonToRemove)
[toolbarButtons removeObject:buttonToRemove];
if (![toolbarButtons containsObject:buttonToAdd])
[toolbarButtons insertObject:buttonToAdd atIndex:4];
[self.toolbar setItems:toolbarButtons];
}
}
Appreciate your help with this.
Check the following:
If the images are stored inside your image assets, make sure they are template images.
Then you can either A) set their tint colour in Interface Builder to the grey colour you prefer or B) do it programmatically in your makeBarButton method as such;
button.tintColor = [UIColor grayColor];

How to pass a UIButton's frame and origin between ViewControllers

I want to do an animation that zooms in from a calendar, specifically with the origin and frame size being that of the button that represent's today's date. Here is the code for determining the todayButton inside CalendarMonthView.m:
NSDate *date = (weekdayOffset > 0) ? [_monthStartDate dateByAddingTimeInterval:-1 * weekdayOffset * D_DAY] : _monthStartDate;
BOOL bEnabled = (weekdayOffset == 0);
CGRect buttonFrame = CGRectMake (0, 0, 81, 61);
int idx = -1 * weekdayOffset;
for (int y = 0; y < 6; y++) {
buttonFrame.origin.x = 0;
for (int x = 0; x < 7; x++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = idx++;
[button setFrame:buttonFrame];
[button setBackgroundImage:[UIImage imageNamed:#"calendarFlyout_dayContainer_today.png"] forState:UIControlStateSelected];
[button setBackgroundImage:[UIImage imageNamed:#"calendarFlyout_selected.png"] forState:UIControlStateHighlighted];
button.titleLabel.font = [UIFont fontWithName:#"TitilliumWeb-Regular" size:18.0];
button.titleLabel.textAlignment = NSTextAlignmentCenter;
// TODO: optimize performance
int dayOfMonth = (int)[_calendar component:NSCalendarUnitDay fromDate:date];
if (dayOfMonth < prevDayOfMonth) {
bEnabled = !bEnabled;
}
prevDayOfMonth = dayOfMonth;
[button setTitle:[NSString stringWithFormat:#"%d", dayOfMonth] forState:UIControlStateNormal];
[button setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
[button setBackgroundImage:[UIImage imageNamed:((bEnabled) ? #"calendarFlyout_dayContainer_insideMonth.png"
: #"calendarFlyout_dayContainer_outsideMonth.png")]
forState:UIControlStateNormal];
// button.enabled = bEnabled;
button.selected = [date isToday];
if (button.selected == NO) {
button.highlighted = (_currentDayDate) ? [date isEqualToDateIgnoringTime:_currentDayDate] : NO;
} else {
// Set buttonHolder to today
}
[button addTarget:self action:#selector (dayButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[_dayButtonsHolderView addSubview:button];
buttonFrame.origin.x += buttonFrame.size.width;
date = [date dateByAddingTimeInterval:D_DAY];
}
buttonFrame.origin.y += buttonFrame.size.height;
}
- (IBAction)dayButtonTapped:(id)sender
{
if (_delegate) {
UIButton *button = (UIButton *)sender;
NSDate *selectedDate = [_monthStartDate dateByAddingDays:button.tag];
[_delegate performSelector:#selector (calendarMonthView:dateSelected:) withObject:self withObject:selectedDate];
}
}
I want to get the frame of button and use it in this animation used in CalendarFlyoutView.m.
I'm new to iOS programming and I read up on some delegate information and passing information through segues, but it doesn't seem to help me here.
Any help would be greatly appreciated.
Use the transform feature.
// animate in year calendar
_yearCalendarView.hidden = NO;
self.curMonthView.monthLabel.hidden = YES;
CalendarMonthView *monthView = [CalendarMonthView new];
monthView.delegate = self;
CGRect buttonFrame = _currentDayButtonFrame;
_yearCalendarView.frame = CGRectMake(buttonFrame.origin.x, buttonFrame.origin.y + buttonFrame.size.height, buttonFrame.size.width, buttonFrame.size.height);
NSLog(#"_yearCalendarView frame: %#", NSStringFromCGRect(_yearCalendarView.frame));
_yearCalendarView.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView animateWithDuration:kAnimation_ExpandCalendarDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
_yearCalendarView.transform = CGAffineTransformIdentity;
_yearCalendarView.frame = CGRectMake(_monthSwiperScrollView.frame.origin.x, _monthBarImageView.frame.size.height + 20, _monthSwiperScrollView.frame.size.width + 2, _yearFlyoutHeight);
} completion:^(BOOL finished) {
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector (yearCalendarAnimationDidStop:finished:context:)];
}];
If you are making a segue from the CalendarMonthView to the CalendarFlyoutView then you can just add this method to the CalendarMonthView.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
CalendarFlyoutView * view = segue.destinationViewController;
view.buttonFrame = button.frame;
}
and in your CalendarFlyoutView.h
#interface CalendarFlyoutView : UIViewController
#property CGRect buttonFrame;
#end
A couple of questions that need to be answered to best help you:
1) Are CalendarMonthView and CalendarFlyoutView UIView or UIViewController? You say view controller, but the class name says otherwise.
2) What is happening in the method 'dayButtonTapped'? Are you performing a segue there? Are you somehow creating or loading an instance of CalendarFlyoutView?
3) If you are not using a segue, are you somehow creating and add a child view controller for CalendarFlyoutView to CalendarMonthView?
4) What is the desired transition (your animation)? Does the CalendarFlyoutView start at the same size as the button, then fill the whole screen?
5) Is there a specific reason that you are setting the button's frame instead of constraining it in the correct place?
Once we know a little more about these questions we should be able to provide some more concrete suggestions.
It's still not very clear where the actual animation code is being run. In your question, you state "I want to get the frame of button and use it in this animation used in CalendarFlyoutView.m." which seems to imply the animation code is in CalendarFlyoutView, which seems like an odd place for it.
There are a few things I could recommend, given my incomplete understanding of the whole of the code:
1) Move the animation code to the dayButtonTapped method, where you already have a reference to the button, and therefore it's frame. It also seems like a more logical place for it.
2) Where ever you are instantiating the CalendarFlyoutView (not shown in the code you posted), assign the button frame there.
3) If there is only a single instance of CalendarFlyoutView and you have a reference to it in CalendarMonthView, you could assign a property of the flyout view that is the frame of the button you already have a reference to in the dayButtonTapped method.
I have to add though, that these are just work arounds to what seems like a structural issue. Is this a reusable control you are trying to write? Why did you decide to not use the storyboard or view controllers? Why are you using frames and absolute sizes and positions at all instead of letting auto layout do it's job (you will bang your head against the wall for hours trying to tweak everything to run correctly in all orientations and all possible device screen sizes).

How Do I Load or Update a View From NSObject Class After Getting the Data?

I'm having a bit of trouble updating my UIImageView to contain subviews of buttons after I'm getting the CGPoint data from another class.
In my main VC, I've got an ImageView set as a subview to a UIScrollView that is a subview of the main ViewControllers view.
In viewDidLoad, I'm calling a method from an NSObject class to get the data for all the button locations from my database:
- (void)parseCoordinateData:(NSArray*)data {
NSLog(#"Processing Data");
mapVC = [[ViewController alloc]init];
for(NSArray *table in data) {
NSLog(#"Table: %#", table);
float xValue = [[NSString stringWithFormat:#"%#", [table[0]objectForKey:#"LocationX"]] floatValue];
float yValue = [[NSString stringWithFormat:#"%#", [table[0]objectForKey:#"LocationY"]] floatValue];
NSLog(#"Coordinates: (%f,%f)", xValue, yValue);
CGPoint buttonCoordinate = CGPointMake(xValue, yValue);
[mapVC placeButtonsFromDatabaseWithCoordinates:buttonCoordinate];
}
}
And in the method from the main VC where I'm placing the buttons:
- (void)placeButtonsFromDatabaseWithCoordinates:(CGPoint)point {
NSLog(#"Placing Button at point: %#", NSStringFromCGPoint(point));
UIButton *button = [[UIButton alloc] init];
[self.imageView addSubview:button];
button.frame = (CGRect){.origin = point, .size = CGSizeMake(20.0, 20.0)};
button.backgroundColor = [UIColor greenColor];
button.layer.cornerRadius = 10;
button.clipsToBounds = YES;
button.userInteractionEnabled = YES;
[self animateButton:button];
[self.imageView bringSubviewToFront:button];
}
But the buttons are not appearing on the image view.
Any idea how to get this to work?
EDIT
I've created a very small project to try and replicate this problem. I've created a view controller that calls to an NSObject class that will call back to the View Controller to draw a subview. I am getting an EXC BAD ACCESS error when attempting this.
However, if handling the call explicitly in the main thread, the app will not crash but the view will not draw.
I think this is what's happening when trying to add the button to the image as a subview.
Change
UIButton *button = [[UIButton alloc] init];
to
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

UIScrollview not scrolling interface builder

I am adding UIScrollView from Interface builder to my xib file. The scrollview is a child of ViewController's view and it also has some other siblings which are on ViewController's view.
Now i am setting the contentSize of the scrollview in viewDidLoad method and also tried to set this in viewDidLayoutSubviews method but the scrollview doesn't scroll. I am sure that the content area of my scrollview is bigger than the frame of the scollview.
Every thing works fine when i add scrollview in my code using the following line
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(20.0, 58.0, 282.0, 200.0)]
but it didn't scroll at all when use the IBOutlet in my code here is my code for viewDidLoad with scollView from Interface builder.
- (void)viewDidLoad
{
[super viewDidLoad];
ALog();
NSUInteger i = 0;
float margin = 0.0;
UIImage *previewImg = nil;
for (i = 0; i < [[_settingsDict objectForKey:#"fullscreenWidgetIDsArray"] count]; i++) {
previewImg = [UIImage imageNamed:[NSString stringWithFormat:#"image_%#", _dict[#"images"][i]]];
if (previewImg == nil) {
[UIImage imageNamed:#"image_2.png"];
}
UIImage *backgroundImg = [UIImage imageNamed:#"image_border.png"];
UIImage *backgroundHighlightedImg = [UIImage imageNamed:#"image_selected.png"];
BOOL selected = [_dict[#"images"][i] unsignedIntegerValue] == [_dict[#"ID"] unsignedIntegerValue];
UIImageView *gridElementView = [[UIImageView alloc] initWithImage:backgroundImg];
[gridElementView setHighlightedImage:backgroundHighlightedImg];
[gridElementView setHighlighted:selected];
gridElementView.userInteractionEnabled = YES;
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(6, 5, previewImg.size.width, previewImg.size.height);
[button setImage:previewImg forState:UIControlStateNormal];
button.contentMode = UIViewContentModeCenter;
margin = (_scrollView.frame.size.width - kElements_per_row * (gridElementView.frame.size.width + 2*kPreview_border_size))/3;
gridElementView.frame = CGRectMake(margin + i%2*(margin + gridElementView.frame.size.width + 2*kPreview_border_size), margin + i/2*(margin + gridElementView.frame.size.height + 2*kPreview_border_size), gridElementView.frame.size.width + 2*kPreview_border_size, gridElementView.frame.size.height + 2*kPreview_border_size);
button.tag = gridElementView.tag = [_settingsDict[#"fullscreenWidgetIDsArray"][i] unsignedIntegerValue];
[button addTarget:self action:#selector(previewBtnPressed:) forControlEvents:UIControlEventTouchUpInside];
[_thumbnailsViewsArray addObject:gridElementView];
[gridElementView addSubview:button];
[_scrollView addSubview:gridElementView];
}
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, margin + (i/2)*(margin + previewImg.size.height + 2*kPreview_border_size));
NSLog(#"contentsize : %#",NSStringFromCGSize(_scrollView.contentSize));
}
In this code i am adding UIButtons inside scrollview in two columns. There are total 6 UIButtons that needs to be added in scrollview. These button were added but scrollview will not scroll at all. Making the scrollEnabled property to yes also didn't work.
Any help will be great.
Thanks

Image view not responding to touch event

I have a grid of thumbnail images, and when one of them is touched, I'd like to show the whole image. Now, I know that UIImageView does not respond to touch events, but as suggested in this answer, I created a UIButton to handle the event. See code below:
- (void)displayImage
{
NSUInteger i = 0; // The actual code is different and works; this is just for brevity's sake.
// UILazyImageView is a subclass of UIImageView
UILazyImageView *imageView = [[UILazyImageView alloc] initWithURL:[NSURL URLWithString:[[[self images] objectAtIndex:i] thumbnailUrl]]];
UIButton *imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
[imageButton setFrame:[imageView frame]];
[imageButton addTarget:self action:#selector(imageTapped:) forControlEvents:UIControlEventTouchUpInside];
[imageView addSubview:imageButton];
[[self containerView] addSubview:imageView];
[imageView release];
}
}
- (void)imageTapped:(id)sender
{
NSLog(#"Image tapped.");
// Get the index of sender in the images array
NSUInteger index = 0; // Don't worry, I know. I'll implement this later
FullImageViewController *fullImageViewController = [[[FullImageViewController alloc] initWithImageURL:[NSURL URLWithString:[[[self images] objectAtIndex:index] url]]] autorelease];
[[self navigationController] pushViewController:fullImageViewController animated:YES];
}
Ok. So I've create a custom button, set its frame to match the image frame, told it to respond to touch up inside and how to respond, and added it to the image's subviews. But when I run this, I get nothing. The "Image tapped." doesn't appear in the console, so I know that the message isn't being sent. What am I doing wrong here?
Many thanks for all your help.
by default UIImageView has its userInteractionEnabled property set to NO
add this line
imageView.userInteractionEnabled = YES;
Try
imageView.userInteractionEnabled = YES;
This is a property inherited from UIView, but as per Apple's docs, UIImageView changes its default value to NO, ignoring all events.
In addition to setting imageView.userInteractionEnabled = YES, you can eliminate the button altogether and add a UITapGestureRecognizer to the UIImageView to handle taps. It would look something like:
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
[imageView addGestureRecognizer:recognizer];

Resources