CGRect Center for Ipad - ios

I am creating an image frame using CGRect. I want to center the rectangle that is created.
I've looked on here, as well as in the Apple documentation for the best way to do this, and found CGRectGetMidY and CGRectGetMidX which get the center coordinates.
When I try implementing this into my own code I run into problems. I get a Property size not found on object of type UIIMageView error
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize mySignatureImage;
#synthesize lastContactPoint1, lastContactPoint2, currentPoint;
#synthesize imageFrame;
#synthesize fingerMoved;
#synthesize navbarHeight;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
CGRect mySignatureImageFrame = CGRectMake(
CGRectGetMidX(self.view.frame) - (mySignatureImage.size.width/ 2.0),
CGRectGetMidY(self.view.frame) - (mySignatureImage.size.height / 2.0),
image.size.width,
image.size.height);
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController <UIAlertViewDelegate>
#property (nonatomic, strong) UIImageView *mySignatureImage;
#property (nonatomic, assign) CGPoint lastContactPoint1, lastContactPoint2, currentPoint;
#property (nonatomic, assign) CGRect imageFrame;
#property (nonatomic, assign) BOOL fingerMoved;
#property (nonatomic, assign) float navbarHeight;
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;

Assuming image is of type UIImage then:
CGRect imageFrame = CGRectMake(
CGRectGetMidX(self.view.frame) - (image.size.width / 2.0),
CGRectGetMidY(self.view.frame) - (image.size.height / 2.0),
image.size.width,
image.size.height);
Assuming imageView is of type UIImageView then:
CGRect imageFrame = CGRectMake(
CGRectGetMidX(self.view.frame) - CGRectGetMidX(imageView.frame),
CGRectGetMidY(self.view.frame) - CGRectGetMidY(imageView.frame),
CGRectGetWidth(imageView.frame),
CGRectGetHeight(imageView.frame));

You don't need to calculate the centre point, as it's available for a view anyway:
CGRect superviewBounds = superview.bounds;
imageView.center = CGPointMake(CGRectGetMidX(superviewBounds), CGRectGetMidY(superviewBounds));

- (void)viewDidAppear:(BOOL)animated {
img.center = CGPointMake(CGRectGetMidX([self.view bounds]), CGRectGetMidY([self.view bounds]));
}
/*write code in viewDidApper */

CGPoint (^CGRectGetCenter)(CGRect) = ^(CGRect rect)
{
return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
};
example : The above answer by Mirant

let center = CGPoint(x: rect.midX, y: rect.midY)

Related

Making UIImage redraw on new position on #IB_Designable

I am creating this IB_Designable class. It is like a slider. See picture. Both elements are created with little stretchable UIImages.
I have this red square that is 66x66 pt. This square has to slide in X inside the gray rectangle.
I have create this class:
HEADER
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface MyClass : UIView
// minimum and maximum slider value
#property (assign, nonatomic) IBInspectable CGFloat minimumValue;
#property (assign, nonatomic) IBInspectable CGFloat maximumValue;
#property (assign, nonatomic) IBInspectable CGFloat value;
#end
IMPLEMENTATION
#import "MyClass.h"
#interface MyClass() {
__weak IBOutlet UIView *topContainer;
CGFloat minimumThumbCoordinate;
CGFloat maximumThumbCoordinate;
}
#property (weak, nonatomic) IBOutlet UIImageView *thumb;
#end
#implementation MyClass
- (void)awakeFromNib {
[super awakeFromNib];
// topContainer contains everything
CGRect topContainerBounds = [topContainer bounds];
CGRect thumbBounds = [self.thumb bounds];
CGFloat topContainerWidth = CGRectGetWidth(topContainerBounds);
CGFloat thumbWidth = CGRectGetWidth(thumbBounds);
minimumThumbCoordinate = floorf(thumbWidth / 2.0f);
maximumThumbCoordinate = floorf(topContainerWidth - minimumThumbCoordinate);
}
-(void)setValue:(CGFloat)value {
if ((value < self.minimumValue) || (value > self.maximumValue)) return;
// normalize values
CGFloat minimumValueNormalized = self.minimumValue;
CGFloat maximumValueNormalized = self.maximumValue;
CGFloat desiredValue = value;
if ((minimumValueNormalized < 0) && (maximumValueNormalized > 0)) {
CGFloat absoluteMinimum = fabsf(self.minimumValue);
minimumValueNormalized = 0;
maximumValueNormalized += absoluteMinimum;
desiredValue += absoluteMinimum;
}
CGFloat percentage = desiredValue/maximumValueNormalized;
// find coordinate
CGFloat coordinateRange = maximumThumbCoordinate - minimumThumbCoordinate;
CGFloat relativeCoordinate = percentage * coordinateRange;
CGPoint center = CGPointMake(relativeCoordinate, self.thumb.center.y);
[self.thumb setCenter: center];
}
The problem is that the setValue method does not make the thumb move when I set the value on interface builder... any ideas?
You cannot have components of your thumbnail added in your storyboard. In this case, I suspect your thumb image view outlet is nil while it's being previewed in IB, and thus changing the value it is likely not updating any subview.
You generally get around this by adding the subviews programmatically, e.g. something like:
// MyClass.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface MyClass : UIView
#property (nonatomic, strong) IBInspectable UIImage *thumbnailImage;
#property (nonatomic) IBInspectable CGFloat minimumValue;
#property (nonatomic) IBInspectable CGFloat maximumValue;
#property (nonatomic) IBInspectable CGFloat value;
#property (nonatomic, readonly) CGFloat percent;
#end
And
// MyClass.m
#import "MyClass.h"
#interface MyClass ()
#property (weak, nonatomic) UIImageView *thumbnailView;
#property (nonatomic) CGFloat thumbWidth;
#end
#implementation MyClass
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configureView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self configureView];
}
return self;
}
- (void)configureView {
UIImageView *thumb = [[UIImageView alloc] initWithImage:self.thumbnailImage];
[self addSubview:thumb];
thumb.backgroundColor = [UIColor lightGrayColor]; // in case no image has been set
thumb.clipsToBounds = true; // in case you change your `contentMode`
thumb.contentMode = UIViewContentModeScaleToFill;
_thumbnailView = thumb;
_thumbWidth = 44; // maybe you have another IBInspectable property for this...
[self layoutThumbnail];
}
- (void)setThumbnailImage:(UIImage *)image {
self.thumbnailView.image = image;
}
- (CGFloat)percent {
CGFloat range = self.maximumValue - self.minimumValue;
return range ? (self.value - self.minimumValue) / range : 0;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutThumbnail];
}
- (void)layoutThumbnail {
CGFloat minX = self.thumbWidth / 2;
CGFloat maxX = self.bounds.size.width - self.thumbWidth / 2;
CGRect frame = CGRectMake(self.percent * (maxX - minX) + minX - self.thumbWidth / 2, 0, self.thumbWidth, self.bounds.size.height);
self.thumbnailView.frame = frame;
}
- (void)setValue:(CGFloat)value {
if (value < self.minimumValue)
_value = self.minimumValue;
else if (value > self.maximumValue) {
_value = self.maximumValue;
} else {
_value = value;
}
[self layoutThumbnail];
}
#end
Note, I update the thumbnail frame not only when you change the value, but also upon layoutSubviews. You want to make sure that the thumbnail frame updates if your control changes size at runtime (e.g. user rotates the device and the control changes size). I also made the thumbnail image a property so you could set this in IB, too, if you wanted. But maybe you have some hardcoded/default image that you use. I also thought that the percent that we use for laying out the thumbnail might potentially be useful by the code that used that control, so I exposed that property.
Now, you're using image views, but if all you wanted were rounded corners of the main control and the thumbnail, I wouldn't use images, but rather just round the corners of the layer for the appropriate UIView objects. Also, I was unclear about your intent of certain things (e.g., I'm not sure what your topContainer is referring to ... you wouldn't generally reference views outside the view hierarchy of the designable view). And you reference the top level view being an image view, which you'd do if you wanted a background image there, too.
But those are details that aren't relevant to your broader question. Hopefully this illustrates the idea, that if you create the subviews programmatically, you can see the thumbnail move as you change your IBInspectable value.
I have seen some implementations here where people wanted to define all of the view hierarchy of the designable view with outlets. But in that case, you have a self-contained NIB for all of these subviews. If you search StackOverflow for "IBDesignable NIB", you'll see links to related answers.

Fill MKOverlay with specific color and blending mode

I need to fill MKOverlay with specific color and kCGBlendModeMultiply blending mode. I've implemented MKOverlayRenderer subclass as follows:
#interface MapDimOverlayRenderer : MKOverlayRenderer
#property (nonatomic, strong) UIColor *overlayColor;
#property (nonatomic, strong) CGFloat overlayAlpha;
#end
#implementation MapDimOverlayRenderer
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay {
self = [super initWithOverlay:overlay];
if (self) {
self.overlayAlpha = 0.9;
self.overlayColor = [UIColor hx_colorWithHexString:#"2C3239" alpha:self.overlayAlpha];
}
return self;
}
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextSetFillColorWithColor(context, self.overlayColor.CGColor);
CGContextFillRect(context, [self rectForMapRect:MKMapRectWorld]);
}
#end
The problem is CGContextSetBlendMode doesn't affect to result. I tried to set different modes. The result is always the same as default (kCGBlendModeNormal).

Changing UITextField top inset not working

I subclassed a UITextField:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface TWAdjustmentsTextField : UITextField
#property (nonatomic) IBInspectable NSInteger topInset;
#property (nonatomic) IBInspectable NSInteger leftInset;
#end
#import "TWAdjustmentsTextField.h"
#implementation TWAdjustmentsTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, self.leftInset, self.topInset);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, self.leftInset, self.topInset);
}
#end
The leftInset property seems to work fine, but the topInset property seems to have no effect on UITextFields. Is there another way to adjust the top inset of a UITextField?
Maybe, CGRectMake can solve the problem :
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface TWAdjustmentsTextField : UITextField
#property (nonatomic) IBInspectable NSInteger topInset;
#property (nonatomic) IBInspectable NSInteger leftInset;
#end
#import "TWAdjustmentsTextField.h"
#implementation TWAdjustmentsTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectMake(bounds.origin.x + leftInset, bounds.origin.y + topInset, bounds.size.width - leftInset * 2, bounds.size.height - topInset * 2);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
#end
I can't guarantee of any negative side effects, but this seems to work with what I'm trying to do.
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect rect = bounds;
rect.origin.x += self.leftInset;
rect.origin.y += self.topInset;
return rect;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
CGRect rect = bounds;
rect.origin.x += self.leftInset;
rect.origin.y += self.topInset;
return rect;
}
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
CGRect rect = bounds;
rect.origin.x += self.leftInset;
rect.origin.y += self.topInset;
return rect;
}

How to detect touch on non-transparent parts of overlay image drawn by MKOverlayRenderer

I draw an image on my map as overlay like so (RW example code):
It has a transparent part in the middle (that should let touches through) and non-transparent sides that should catch touches (kind of like a donut :)).
Overlay.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#class PVPark;
#interface PVParkMapOverlay : NSObject <MKOverlay>
- (instancetype)initWithPark:(PVPark *)park;
#end
Overlay.m
#import "PVParkMapOverlay.h"
#import "PVPark.h"
#implementation PVParkMapOverlay
#synthesize coordinate;
#synthesize boundingMapRect;
- (instancetype)initWithPark:(PVPark *)park {
self = [super init];
if (self) {
boundingMapRect = park.overlayBoundingMapRect;
coordinate = park.midCoordinate;
}
return self;
}
#end
OverlayView.h
#import <MapKit/MapKit.h>
#interface PVParkMapOverlayView : MKOverlayRenderer
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage;
#end
OverlayView.m
#import "PVParkMapOverlayView.h"
#interface PVParkMapOverlayView ()
#property (nonatomic, strong) UIImage *overlayImage;
#end
#implementation PVParkMapOverlayView
- (instancetype)initWithOverlay:(id<MKOverlay>)overlay overlayImage:(UIImage *)overlayImage {
self = [super initWithOverlay:overlay];
if (self) {
_overlayImage = overlayImage;
}
return self;
}
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
CGImageRef imageReference = self.overlayImage.CGImage;
MKMapRect theMapRect = self.overlay.boundingMapRect;
CGRect theRect = [self rectForMapRect:theMapRect];
CGContextScaleCTM(context, 1.0, -1.0);
CGContextTranslateCTM(context, 0.0, -theRect.size.height);
CGContextDrawImage(context, theRect, imageReference);
}
#end
I add it to the map like so:
MainViewController
- (void) viewDidLoad
{
[super viewDidLoad];
[self addOverlay];
}
- (void)addOverlay {
PVParkMapOverlay *overlay = [[PVParkMapOverlay alloc] initWithPark:self.park];
[self.mapView addOverlay:overlay];
}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:PVParkMapOverlay.class]) {
UIImage *magicMountainImage = [UIImage imageNamed:#"overlay_park"];
PVParkMapOverlayView *overlayView = [[PVParkMapOverlayView alloc] initWithOverlay:overlay overlayImage:magicMountainImage];
return overlayView;
}
return nil;
}
How can I detect a touch on the non-transparent part of the overlayed image? I've tried using some examples using MKPolyLines but to no avail. Should I create an invisible (to the user) polyline using the cutout's location data and check for that? Seems like there should be a way to catch the overlay more efficiently, no?

implementing a UIGestureRecognizerDelegate

Im trying to get simultaneous pan and pinch from the article below but i dont think the delegate is hooked up properly since im seeing no effect. Right now I have a pan and a pinch gesture recognizer that both work. I make IBOutlets to my viewcontroller, and in my viewController initializer have:
EDIT
here is the header and implementation files respectively.
header:
#import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#class GLView;
#interface GLViewController : UIViewController <UIGestureRecognizerDelegate>
#property (strong, nonatomic) IBOutlet UIPinchGestureRecognizer *pinchRecognizer;
#property (strong, nonatomic) IBOutlet UIPanGestureRecognizer *panRecognizer;
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer;
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer;
- (void)drawView:(GLView*)view;
- (void)setupView:(GLView*)view;
#property (retain, nonatomic) IBOutlet UILabel *zoomFactorLabel;
#property (retain, nonatomic) IBOutlet UILabel *xPosLabel;
#property (retain, nonatomic) IBOutlet UILabel *yPosLabel;
#end
panRecognizer.minimumNumberOfTouches = 1;
panRecognizer.delegate = self; // Very important
pinchRecognizer.delegate = self; // Very important
implementation:
#import "GLViewController.h"
#import "GLView.h"
#import "OpenGLCommon.h"
#import "ConstantsAndMacros.h"
#import "TileManager.h"
#import <CoreGraphics/CoreGraphics.h>
#implementation GLViewController
#synthesize pinchRecognizer;
#synthesize panRecognizer;
#synthesize zoomFactorLabel;
#synthesize xPosLabel;
#synthesize yPosLabel;
TileManager* tileManager;
CGPoint currentPosition;
CGPoint start;
CGFloat temp = 0;
CGFloat x=0;
CGFloat y=0;
CGFloat mLastScale=1;
CGFloat mCurrentScale=1;
CGPoint translation;
- (id) init {
self = [super init];
if (self != nil) {
printf("GLViewController initialized.");
tileManager=[[TileManager alloc] init];
currentPosition = CGPointMake(0, 0);
}
return self;
}
CGFloat scale=1;
CGPoint position;
CGPoint mid;
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizershouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSLog(#"delegate called");
// If you have multiple gesture recognizers in this delegate, you can filter them by comparing the gestureRecognizer parameter to your saved objects
return YES; // Also, very important.
}
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
scale=scale*recognizer.scale;
mCurrentScale += [recognizer scale] - mLastScale;
mLastScale = [recognizer scale];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
//get midpoint
CGPoint zero=[recognizer locationOfTouch:0 inView:self.view];
CGPoint one=[recognizer locationOfTouch:1 inView:self.view];
float x=zero.x+one.x;
float y=zero.y+one.y;
mid.x=x/2;
mid.y=y/2;
}
else if (recognizer.state == UIGestureRecognizerStateEnded)
{
mLastScale = 1.0;
}
NSString *xPosString = [NSString stringWithFormat:#"%.2f",mid.x];
NSString *yPosString = [NSString stringWithFormat:#"%.2f",mid.y];
xPosLabel.text=xPosString;
yPosLabel.text=yPosString;
}
- (IBAction)handlePan:(UIPanGestureRecognizer* )recognizer {
[recognizer setMaximumNumberOfTouches:1];
translation= [recognizer translationInView:self.view];
NSString *xPosString = [NSString stringWithFormat:#"%.2f",currentPosition.x];
NSString *yPosString = [NSString stringWithFormat:#"%.2f",currentPosition.y];
xPosLabel.text=xPosString;
yPosLabel.text=yPosString;
currentPosition.x=currentPosition.x+translation.x;
currentPosition.y=currentPosition.y+translation.y;
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
- (void)drawView:(GLView*)view
{
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glTranslatef(mid.x, -mid.y, 0);
glScalef(mCurrentScale,mCurrentScale, 0);
glTranslatef(-mid.x, mid.y, 0);
glTranslatef(currentPosition.x,currentPosition.y*-1,0);
[tileManager drawView:view];
//draw calls
}
-(void)setupView:(GLView*)view
{
CGRect rect = view.bounds;
glViewport(0, 0,rect.size.width,rect.size.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0,(rect.size.width),-(rect.size.height),0, -1, 1 ) ;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0,1,1, 1);
// Enable Smooth Shading, default not really needed.
glShadeModel(GL_SMOOTH);
// Depth buffer setup.
glClearDepthf(1.0f);
//enable textures.
glEnable(GL_TEXTURE_2D);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
[tileManager setupView:view];
}
- (void)viewDidLoad
{
[super viewDidLoad];
panRecognizer.delegate=self;
pinchRecognizer.delegate=self;
NSLog(#"pan recognizer delegate [%#]", [panRecognizer.delegate description]);
}
- (void)viewDidUnload {
[self setPinchRecognizer:nil];
[self setPanRecognizer:nil];
[super viewDidUnload];
}
#end
a NSLog is in the body of the delegate method but never gets executed. Any help is appreciated.
article
If the view controller is loaded from the interface builder, then the initializer is the wrong place to set that stuff up. Do it in -viewDidLoad.
you probably caught it by now - but it seems that you miss a space in the delegate:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizershouldRecognizeSimultaneouslyWithGestureRecognizer
to
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer

Resources