UIButton not working in a custom UIImageView - objective-c

I am using a custom view and there is a UIButton in it but it is not working. Of course the button is inside the custom view. It's driving me crazy. I tried by self.isUserInteractionEnabled = YES;, but getting an error "No setter method 'setIsUserInteractionEnabled:' for assignment". Can anyone help...
My header file...
#import <Foundation/Foundation.h>
#import "Clone.h"
#class UICloneInfoView;
#interface UICloneInfoView : UIImageView
{
Clone *clone;
}
#property(nonatomic, retain) Clone *clone;
#property(nonatomic, retain) UIButton *button;
#property(nonatomic, retain) UILabel *infoLabel;
// set a clone to show information on the bar
- (void)setClone:(Clone *)aClone;
#end
Here is the .m file...
#import "UICloneInfoView.h"
#define DEFAULT_HEIGHT_VIEW 90.0f
#define DEFAULT_WIDTH_VIEW 819.0f
#define DEFAULT_BUTTON_SIZE 40.0f
#implementation UICloneInfoView
#synthesize clone = _clone;
#synthesize button = _button;
#synthesize infoLabel = _infoLabel;
- (id)initWithImage:(UIImage *)image
{
self = [super initWithImage:image];
if (self) {
// init somthing
// init info label
_infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 5.0f, DEFAULT_WIDTH_VIEW - 80.0f, DEFAULT_HEIGHT_VIEW)];
_infoLabel.backgroundColor = [UIColor clearColor];
_infoLabel.font = [UIFont boldSystemFontOfSize:13.0f];
_infoLabel.lineBreakMode = UILineBreakModeCharacterWrap;
_infoLabel.numberOfLines = 3;
// init button
_button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_button.frame = CGRectMake(_infoLabel.frame.origin.x + _infoLabel.frame.size.width + 10.0f, (self.frame.size.height/2), DEFAULT_BUTTON_SIZE, DEFAULT_BUTTON_SIZE);
[_button setTitle:#"1" forState:UIControlStateNormal];
[_button addTarget:self action:#selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_button];
}
return self;
}
- (void)setClone:(Clone *)aClone
{
_clone = aClone;
// set label text
_infoLabel.text = [NSString stringWithFormat:#"Start Line: %d\tEnd Line: %d\t nLines: %d\tpcid: %d\nFile: %#", _clone.startLine, _clone.endLine, _clone.endLine - _clone.startLine + 1, _clone.pcid, _clone.sourcePath];
// adjust the label with new height
CGSize expectedLabelSize = [_infoLabel.text sizeWithFont:_infoLabel.font constrainedToSize:_infoLabel.frame.size lineBreakMode:_infoLabel.lineBreakMode];
CGRect newFrame = _infoLabel.frame;
newFrame.size.height = expectedLabelSize.height;
_infoLabel.frame = newFrame;
// add _infoLabel to view
[self addSubview:_infoLabel];
}
- (void)buttonAction:(id)sender
{
NSLog(#"%#", ((UIButton*)sender).titleLabel.text);
}
#end
Here is a snapshot
Thanks in advance.

UIImageView has userInteractionEnabled set to NO by default. You are adding the button as a subview to the image view. You should set it to YES.

It might be because your class is a subview of UIImageView, so the image view is handling all the touch events, and they are never reaching your button. You could play around with disabling user interaction on the image view so that the touch event gets to the button.
A far simpler solution would just be to make UICloneInfoView a subclass of UIView instead. Then you can add your button, your labels, Clone and then just add a UIImageView as a subview.
Is there any reason you wanted to subclass UIImageView? I don't know your requirements.

Related

How to set fill color property of custom UIView (a circle) from View Controller

I'm guessing I don't know enough about Quartz or CAShapeLayer manipulations, but I wanted to know how to change the fill color to custom UIView that I have.
Here's the implementation for DotView:
#define kCustomBlue [UIColor colorWithRed:181.0/255 green:228.0/255 blue:226.0/255 alpha:1]
#implementation DotView
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if ((self = [super initWithCoder:coder])) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
}
return self;
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
[circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:rect] CGPath]];
circleLayer.fillColor = kCustomBlue.CGColor;
[self.layer addSublayer:circleLayer];
}
#end
After placing a DotView object ((nonatomic, weak) IBOutlet in my ViewController interface) in my Storyboard, I run the code and everything is fine and dandy. That object in my ViewController interface is called dot1
I want to have a property #property (nonatomic, strong) UIColor* fillingColor; which sets the fill color for the view. How do I implement that correctly?
The idea is this: there is a tap gesture recognizer object attached to the dot1 view, and everytime I tap the dot, the color changes from blue to black (then black to blue).
I'm using XCode 9.3 and have an iPhone 7 running iOS 11.2
Thanks, Anthony
Add the property to the .h:
#interface DotView: UIView
// Add this to everything else you have
#property (nonatomic, strong) UIColor* fillingColor;
#end
Then in the .m, override the setter:
- (void)setFillingColor:(UIColor *)color {
_fillingColor = color;
[self setNeedsDisplay];
}
Then in drawRect:, use your fillingColor property for the fill color:
circleLayer.fillColor = self.fillingColor.CGColor;
Please note that you do not want to be adding layers over and over every time drawRect: is called. You don't even need to use layers for this. Just fill the UIBezierPath.

add GLKViewController to subview - GLKView context causes crash

I have a pretty simple set up in mind, having a mainViewController that has a GLKViewController on top of it. The idea is having my GLKViewController in a box, that take sup 1/3 of the screen, on the mainViewController. This can be seen below:
That white box is my own custom GLKViewController with the follow code:
boxViewController.h
//boxViewController.h
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#interface boxViewController : GLKViewController
#end
boxViewController.m
//boxViewController.m
#import "boxViewController.m"
#interface boxViewController () { }
#property (strong, nonatomic) EAGLContext *context;
#end
#implementation boxViewController
-(void)viewDidLoad {
[super viewDidLoad];
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!self.context) {
NSLog(#"Failed to create ES context");
}
GLKView *view = (GLKView *)self.view;
// view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
}
#end
On my mainViewController in the viewDidLoad I simply call boxViewController like this:
boxViewController* box = [[boxChartViewController alloc] init];
box.view.layer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, self.view.frame.size.height/3);
[self.view addSubview:box.view];
which works perfect.
Notice that in my boxViewController.m I had view.context = self.context commented out. If you uncomment it, my application crashes without any error messaging (it breaks with a EXC_BAD_ACCESS in the objc_msgSend assembly code [line 8 to be specific]).
What am I doing incorrectly that when I set the context the application crashes? From all the tutorials I noticed that they have the same set up, except not setting the controller on another controller. Though I don't understand why GLKViewController couldn't be framed on another controller, so I don't think that's the issue.
After a few hours of messing around I found that adding the viewController as a child works:
#import "mainViewController.h"
#implementation mainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.layer.backgroundColor = [UIColor colorWithRed:242.0f/255.0f green:242.0f/255.0f blue:242.0f/255.0f alpha:1.0].CGColor;
boxViewController* chart = [[boxViewController alloc] init];
chart.view.layer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, self.view.frame.size.height/3);
chart.view.layer.borderColor = [UIColor blackColor].CGColor;
chart.view.layer.borderWidth = 2.0f;
[self addChildViewController:chart];
[self.view addSubview:chart.view];
}

Objective C: Download Thread not affecting UI, even possible?

I'm working through the Standford course on ItunesU and there they say a thread to download something should not do anything to the UI, this should only happen on the Main Thread.
Well, in my example I'm downloading a picture from Flicker and I want to setup this picture (via a segue) in a UIScrollView. So while I'm downloading this picture in the "side" thread I'm setting the image property of the UIScrollview to image etc. However this doesn't work obviously, because I don't know the imagesize yet and I also don't have a reference to that image object yet to set right?
So how do you handle that? I hope I'm clear..here is my example:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(NSIndexPath *)sender{
NSDictionary *selectedPhoto = [self.photos objectAtIndex:sender.row];
[self.defaults addPhotoToRecentlyViewed:selectedPhoto];
[self.defaults saveDefaults];
PhotoViewer *photoViewer = segue.destinationViewController;
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
photoViewer.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:spinner];
[spinner startAnimating];
dispatch_queue_t photoDownload = dispatch_queue_create("photoviewever", nil);
dispatch_async(photoDownload, ^{
NSData *data = [NSData dataWithContentsOfURL:[FlickrFetcher urlForPhoto:selectedPhoto format:FlickrPhotoFormatLarge]];
UIImage *image = [UIImage imageWithData:data];
photoViewer.image = image;
dispatch_async(dispatch_get_main_queue(), ^{
photoViewer.title = [selectedPhoto objectForKey:FLICKR_PHOTO_TITLE];
photoViewer.navigationItem.rightBarButtonItem = nil;
});
});
}
and my PhotoViewer:
#import "PhotoViewer.h"
#interface PhotoViewer ()
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) IBOutlet UIImageView *imageView;
#end
#implementation PhotoViewer
#synthesize scrollView = _scrollView;
#synthesize imageView = _imageView;
#synthesize image = _image;
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.delegate = self;
[self setupImage];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
return self.imageView;
}
- (void)setupImage{
self.imageView.image = self.image;
self.imageView.frame = CGRectMake(0, 0, self.image.size.width, self.image.size.height);
self.scrollView.contentSize = self.image.size;
[self.imageView setNeedsDisplay];
}
#end
You should move this line:
photoViewer.image = image;
into the "completion" block you dispatch back to the main thread. PhotoViewer appears to be a view controller, and as such it's subject to the same "main thread only" rules as other UI. Also, it looks like you'll need to call -setupImage again from that main thread "completion" block. (Otherwise the image will never get pushed into the image view, as you noted.)
Also in -setupImage, you want to check that self.image returns a non-nil value before addressing into it. Depending on which compiler you're using, the behavior of calling a struct-returning Objective C method is 'undefined' (i.e. self.image.size returns a CGSize struct). (Although in recent compilers, it returns a zero filled struct.)

Changing an image in MKMapKit MKAnnotation Subclass

I am having some problems with changing an image view inside of an MKAnnotationView subclass. I have a timer that updates the annotation position based on data about where a user was at a certain point. This works fine and the annotation position is updated correctly. I do this with the following code:
[_myAnnotation setCoordinate:point.position];
Below this, I also calculate whether the user was heading east or west. If the user is heading east, I call the setIsMovingRight:YES method on the MyAnnotation class, and do the opposite for when the user is heading west.
I have verified that the method is being called with the correct values at the correct times by using NSLog inside the method to print out whenever the value changes. However, it doesn't seem like the setIsMovingRight method has any effect on the visual appearance of the annotation. I try setting the background colour of the imageview to red whenever the method is called, but even this has no effect. I have tried calling setNeedsDisplay in various places with no success.
I can add and recreate an annotation view, but then the annotation flashes on and off whenever the position changes and it really does not look very good.
The following is the code for my custom annotation view, "MyAnnotation".
#interface MyAnnotation : MKAnnotationView <MKAnnotation>
{
UIImageView *_imageView;
BOOL _currentlyMovingRight;
}
#property (nonatomic) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic,retain) UIImage *image;
-(void)setIsMovingRight:(BOOL)movingRight;
#end
#implementation MyAnnotation
#synthesize coordinate, title, subtitle, image;
-(id)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if(self)
{
_currentlyMovingRight = NO;
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"right.png"]];
[self addSubview:_imageView];
}
return self;
}
-(void)setIsMovingRight:(BOOL)movingRight
{
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[_imageView setBackgroundColor:[UIColor redColor]]; // THIS LINE NEVER DOES ANYTHING.
if(movingRight && !_currentlyMovingRight)
{
NSLog(#"right now.");
[_imageView setImage:[UIImage imageNamed:#"right.png"]];
}
else if (!movingRight && _currentlyMovingRight)
{
NSLog(#"left now.");
[_imageView setImage:[UIImage imageNamed:#"left.png"]];
}
_currentlyMovingRight = movingRight;
[self addSubview:_imageView];
[self setNeedsDisplay];
}
I would appreciate any help or advice that anyone could give. Thanks!
I think that the annotation views use KVO to listen for changes. Have you tried changing the image property? Something like [self setImage:myNewImage]

How to Implement ScrollView In TableViewCell

Hey guys I'm trying to implement a scroll with page control, like the iPhone home screen, but in a uitableview cell.
What I tried to do to attempt this is create a custom table view cell with a xib file, and placed the uipagecontrol and uiscrollview on it, and connected it with iboutlets to the uitableviewcell.
This is the code in the .h file for the custom cell.
#interface ScrollableCell : UITableViewCell <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) IBOutlet UIPageControl *pageControl;
#property (nonatomic, strong) NSMutableArray *viewControllers;
This is the code from the .m file for the custom cell after synthesizing the properties.
- (CGSize)contentSizeForPagingScrollView {
CGRect bounds = self.scrollView.bounds;
return CGSizeMake(bounds.size.width * [self.viewControllers count] , bounds.size.height );
}
#- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
UIView *blueView = [[UIView alloc] initWithFrame:CGRectZero];
[blueView setBackgroundColor:[UIColor blueColor]];
UIView *redView = [[UIView alloc] initWithFrame:CGRectZero];
[redView setBackgroundColor:[UIColor redColor]];
UIView *yellowView = [[UIView alloc] initWithFrame:CGRectZero];
[yellowView setBackgroundColor:[UIColor yellowColor]];
self.viewControllers = [NSArray arrayWithObjects:blueView, redView, yellowView,nil];
for (UIView *view in self.viewControllers) {
[view setFrame:CGRectMake([self.viewControllers indexOfObject:view]*self.scrollView.frame.size.width+5,
5,
self.scrollView.frame.size.width-10,
self.scrollView.frame.size.height-10.0)];
[self.scrollView addSubview:view];
}
self.pageControl.numberOfPages = [self.viewControllers count];
self.pageControl.currentPage = 0;
self.scrollView.pagingEnabled = YES;
self.scrollView.backgroundColor = [UIColor blackColor];
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.contentSize = [self contentSizeForPagingScrollView];
self.scrollView.delegate = self;
[self.contentView addSubview:self.scrollView];
[self.contentView addSubview:self.pageControl];
}
return self;
}
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
Hope you guys can help me get this working. If there is a different approach/ better approach that what I'm attempting, let me know.
For visual reference of what I'm trying to achieve, i think the pulse news app fits the bill
http://itunes.apple.com/us/app/pulse-news-for-iphone/id377594176?mt=8
its a table view, with horizontal scrolling on each cell.
Thanks.
If there is a different approach/ better approach that what I'm attempting, let me know.
For visual reference of what I'm trying to achieve, i think the pulse news app fits the bill.
If your main reference for designing this is the Pulse app, you should know that the developers actually did NOT resort to scrollviews. The Pulse app is all tableviews. One main vertical tableview and several horizontal "hacked" tableviews inside each cell that composes the vertical one.
Its quite a smart implementation for a tableview. But who better to explain this to you than the developers of Pulse themselves; they were invited to a lecture at Stanford for their iOS programming course.
Its on iTunes U and it's at 6 min in the lecture called 10 Hacks to Go From Idea To #1 App. You should watch it and maybe rethink your app design if it suits you better.