Changing an image in MKMapKit MKAnnotation Subclass - objective-c

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]

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.

Making a UIButton change a UIImageView image

I'm very new to programming and objective c so please go easy on me.
I would like a UIButton (which I'm using as an IBAction) to change an the image in a UIImageView when pressed. I put the UIImageView into a UIScrollView in the storyboard but I hope I can still programmatically change the image.
I have searched absolutely everywhere for an answer for hours but nothing has worked for me either because it wasn't the right solution or I didn't do it properly.
I have tried this code and some more but they always return a "Thread 1: signal SIGABRT" when I press the button:
imageView.image = [UIImage imageNamed: #"Ruler pic inch.png"];
Here is my code so far:
ViewController.h
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#interface ViewController : UIViewController <ADBannerViewDelegate, UIScrollViewDelegate> {
ADBannerView *adView;
BOOL bannerIsVisible;
IBOutlet UIScrollView *scrollView;
IBOutlet UIImageView *imageView;
IBOutlet UIButton *proVersion;
IBOutlet UIButton *howToUse;
}
- (IBAction)switchUnit:(id)sender;
#property (nonatomic, assign) BOOL bannerIsVisible;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize bannerIsVisible;
#synthesize scrollView;
#synthesize imageView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Hide status bar:
[[UIApplication sharedApplication] setStatusBarHidden:YES];
// iAd:
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, -50.0f);
[self.view addSubview:adView];
adView.delegate=self;
self.bannerIsVisible=NO;
// Setting scrollview content size to size of image
scrollView.contentSize = CGSizeMake(320,2246);
}
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOn" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, 50.0f);
[UIView commitAnimations];
self.bannerIsVisible = YES;
}
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible) {
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, -50.0f);
[UIView commitAnimations];
self.bannerIsVisible = NO;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)switchUnit:(id)sender {
// CODE THAT MAKES IMAGE CHANGE GOES HERE (I thought...)
}
#end
Help would be greatly appreciated. I'm fed up with it. I've probably done a silly mistake somewhere but I can't work it out. Thanks in advance.
General pointers:
Try to use #property for all your instance variables (it'll server you well in future)
Don't use #synthesize (the compiler does a better job of it for you)
If you have a property bob, access it by using self.bob
Turn on ARC (looks like you don't have it on currently)
Best not to have any spaces in image names
To be clear on properties, when you have a property you don't need to create instance variables in between {} of the #interface. These should be removed in order to prevent multiple definitions. The auto-synthesising done by the compiler will define the instance variables for you but you should always use the property to access the value (as in item 3 above).
That said, your - (IBAction)switchUnit:(id)sender looks fine, if a little empty. The code should be something like:
self.imageView.image = [UIImage imageNamed:#"Ruler_pic_inch.png"];
For it to work, the image Ruler_pic_inch.png would need to be in your bundle (which means it's in Xcode project and set to copy during the build.
Again, that said, your problem would seem to relate to you being confused somewhere. SIGABRT type issues would usually be picked up by the compiler and notified to you as a warning saying something like "some object may not respond to some selector". If you see any of those, look at them and work out why you're trying to ask the object a question it doesn't understand.
If you don't see any compiler warnings, then you've told the compiler that an object is going to be of one type and then actually set it to something else. In this case I'd look at your IBOutlets in Storyboard and check that they are actually set to the correct destination view (disconnect and reconnect them all to be sure).

Clickable search icon in NSSearchField and fading background

I would like to make an NSSearchField, just like the one in Ecoute 3.0.
The search icon in the search field should be clickable and expand and collapse if you click it.
How can you achieve this?
I have searched the documentation, but didn't come up with anything useful.
I could implement the expanding with the animator proxy, so that is clear.
Another thing is that the background fades in/fades out as it expands/collapses.
How can I achieve this?
I would be thankful for any advices, sample code, etc.
EDIT
Ok I found the first part out myself.
- (void) resetSearchButtonCell {
NSButtonCell *c= [[NSButtonCell alloc] init];
[c setButtonType:NSMomentaryChangeButton]; // configure the button
[c setBezelStyle:NSRegularSquareBezelStyle]; // configure the button
[c setBordered:NO];
[c setBezeled:NO];
[c setEditable:NO];
[c setImagePosition:NSImageOnly];
[c setImage:[NSImage imageNamed:#"search"]];
[c setAlternateImage:[NSImage imageNamed:#"search-pushed"]];
[c setTarget:self];
[c setAction:#selector(_search:)];
[self setSearchButtonCell:c];
}
Now, I have made a property isCollapsed, which I toggle in _search:.
And in the setIsCollapsed: setter method I do the following:
NSRect newRect = self.frame;
if (_isCollapsed) {
newRect.size.width = kCollapsedWidth;
} else {
newRect.size.width = kEXpandedWidth;
}
[[self animator] setFrameSize:newRect.size];
But for some reason the frame is being reset to the initial size, the one set in Interface Builder, when the NSSearchField gets or looses focus.
Can anyone tell me why?
EDIT
I have solved that problem too. It was auto-layout, which messed up the animation.
Now, the only thing remaining is the background-alpha, which should change.
I could redraw the entire thing, but is there another solution where I could just modify the alpha of only the background and the cancel-button?
I wanted to do something similar. ITSearchField is very solid and nice. However, I desired to expand the search field once it gained focus. As one might find out quickly the NSSearchfield is comprised of many components and layers.
Here is what I wrote to get it to work...
Note: It works on 10.7 or greater. Also you must hook-up the layout constraint of the search field control's width in IB to the outlet.
.h
#interface MySearchField : NSSearchField
#end
.m
#pragma mark - Implementation: Search Field Control
#interface MySearchField()
#property (readwrite, nonatomic, getter=isExpanded) BOOL expand;
#property (readwrite, nonatomic, getter=hasFocusRing) BOOL focusRing;
#property (readwrite, strong, nonatomic) NSTimer *expandTimer;
#property (readwrite, strong, nonatomic) IBOutlet NSLayoutConstraint *widthConstraint;
#property (readwrite, nonatomic) CGFloat searchWidth;
#end
static NSTimeInterval const kSearchFieldTime = 0.5;
#implementation MySearchField
#synthesize expand, focusRing;
#synthesize expandTimer, searchWidth;
#synthesize widthConstraint;
#pragma mark Timer Methods:
- (void) resizeSearchField {
if ( self.hasFocusRing ) {
if ( !self.isExpanded ) {
self.searchWidth = self.widthConstraint.constant;
[[self.widthConstraint animator] setConstant:(self.searchWidth * 2)];
self.expand = YES;
} } else {
if ( (self.isExpanded) &&
((!self.stringValue) || ([self.stringValue isEqualToString:#""])) ) {
[[self.widthConstraint animator] setConstant:self.searchWidth];
self.expand = NO;
} }
}
- (void) stopTimer {
if ( self.expandTimer )
[self.expandTimer invalidate];
self.expandTimer = nil;
}
- (void) startTimer {
[self stopTimer];
SEL resizeSelector = #selector(resizeSearchField);
NSMethodSignature *expandSignature = [MySearchField instanceMethodSignatureForSelector:resizeSelector];
NSInvocation *expandInvocation = [NSInvocation invocationWithMethodSignature:expandSignature];
[expandInvocation setSelector:resizeSelector];
[expandInvocation setTarget:self];
self.expandTimer = [NSTimer scheduledTimerWithTimeInterval:kSearchFieldTime invocation:expandInvocation repeats:NO];
}
#pragma mark Override Methods:
- (void) noteFocusRingMaskChanged {
self.focusRing = !NSEqualRects(NSZeroRect, self.focusRingMaskBounds);
[self startTimer];
[super noteFocusRingMaskChanged];
}
#end
Ok that's it really.
You get notified when the focus ring changes and start a quick timer method.
The timer is necessary for when you click the search field's cancel button which resets the control.
The only thing the timer does is fire the resizeSearchField method.
Update: Oh yeah, don't forget to set the class of your Search Field control in IB to MySearchField or whatever you want to call it.
Enjoy!
I ended up redrawing the background. Like this I was able to set the alpha of it.
The source code is available on Github:
https://github.com/iluuu1994/ITSearchField

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.)

Why isn't my UIButton responding to touches?

I'm sure I'm overlooking the obvious as I've got countless working buttons...but...for whatever reason this one is not cooperating...
I've added a UIButton (Rounded Rect) to a UIView subclass (DialogView) which is a subview of my view controller's view. This subview is created almost entirely in IB. I've wired up the button to (IBAction)okButtonPressed:(id)sender in IB to Touch Up Inside and created a corresponding method in DialogView. However when I "touch" this button it doesn't trigger the method. userInteractionEnabled is true for the VC's view, DialogView and the UIButton.
Thinking maybe initWithCoder had to do some frame manipulation or something I added the following which successfully logs to console.
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super initWithCoder:decoder]) {
NSLog(#"DialogView initWithCoder called");
}
return self;
}
In further exploration I wired up an IBOutlet to the button and then if I try to change the titleLabel from the view controller I notice that it get's severely truncated. Default text of say "Press Me!" set in IB displays fine when view is first drawn. But if I change the text...
self.DialogView.okButton.titleLabel.text = #"Not Working";
...it gets truncated to "N..."
Dunno if this is related. Probably...
Anyone see what I've screwed up here?
Edit (adding code related to showing UIButton):
From the View Controller:
self.DialogView = [[[NSBundle mainBundle] loadNibNamed:#"DialogView" owner:self options:nil] objectAtIndex:0];;
self.DialogView.myVC = self;
self.DialogView.backgroundColor = [UIColor clearColor];
self.DialogView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
self.DialogView.nameLabel.text = loan.fullName;
self.DialogView.noteLabel.text = loan.summaryOfLoan;
self.DialogView.amountLabel.text = [currencyFormatter stringFromNumber:loan.originalAmount];
self.DialogView.alpha = 0.0;
[self.view addSubview:DialogView];
The UILabels all displaying as expected. As is the problem UIButton. I can see it I just can't interact with it!?!
DialogView's interface:
#class MyViewController;
#interface DialogView : UIView {
IBOutlet UILabel *nameLabel, *noteLabel, *amountLabel;
IBOutlet UIImageView *arrowView;
IBOutlet UIButton *okButton;
MyViewController *myVC;
}
#property (nonatomic, retain) UILabel *nameLabel, *noteLabel, *amountLabel;
#property (nonatomic, retain) UIImageView *arrowView;
#property (nonatomic, assign) MyViewController *myVC;
#property (nonatomic, retain) UIButton *okButton;
- (IBAction)okButtonPressed:(id)sender;
#end
And DialogView's implementation:
#import "DialogView.h"
#import "MyViewController.h"
#implementation DialogView
#synthesize nameLabel, noteLabel, amountLabel, arrowView, okButton;
#synthesize myVC;
- (void)dealloc {
[nameLabel release];
[noteLabel release];
[amountLabel release];
[arrowView release];
[okButton release];
[super dealloc];
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super initWithCoder:decoder]) {
NSLog(#"DialogView initWithCoder called");
}
return self;
}
- (IBAction)okButtonPressed:(id)sender {
NSLog(#"pressed DialogView OK button");
[self.myVC.navigationController popViewControllerAnimated:YES];
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
#end
I thought that we should use -setTitle:forState: in order to set button's title ?
An other thought, did you check that the button's frame is not CGRectZero ? And by the way, all the frames for the view in the hierarchy ? And check that one superview in the hierarchy is not user interaction disabled ?
And, I think imageView does not respond to touches, do you have one in your code ?
I was just having more or less the same problem and I found that my containing view did not have "User Interaction Enabled".
Hope this helps.
Do you maybe have two buttons on top of one another? Change the IB project window to the detail view and see if your view has more buttons than you are expecting. Maybe you've wired up a button that's not actually getting the press you're expecting.