Trying to set UIControl objects' frames through a function - objective-c

I'm trying to create a function to change the size of something like a UILabel or UIButton without having to type the three lines out every time. This is what I have.
-(void)setObject:(UIControl*)object SizeWidth:(NSInteger)width Height:(NSInteger)height
{
CGRect labelFrame = object.frame;
labelFrame.size = CGSizeMake(width, height);
object.frame = labelFrame;
}
However, when I give (UIControl*)object a UILabel, it says "incompatible pointer types". How can I fix this to work for anything I can put on a UIView?

UILabel is not a subclass of UIControl, it inherits from UIView.
Try changing UIControl to UIView:
-(void)setObject:(UIView*)object SizeWidth:(NSInteger)width Height:(NSInteger)height
{
CGRect labelFrame = object.frame;
labelFrame.size = CGSizeMake(width, height);
object.frame = labelFrame;
}
(UIControl inherits from UIView anyway, and frame is a UIView property)

Label is not a subclass of UIControl. You can use UIView in place of UIControl.
Here is the hierarchy for UILabel
UILabel: UIView : UIResponder : NSObject
-(void)setObject:(UIView*)object SizeWidth:(NSInteger)width Height:(NSInteger)height
{
CGRect labelFrame = object.frame;
labelFrame.size = CGSizeMake(width, height);
object.frame = labelFrame;
}
One suggestion for you is, the method name seems kind of odd to me. You can write a simple category to update the size for UIView. With the following category you can simply call
[myLabel setWidth:20 andHeight:20];
In UIView + MyCategory.h
#import <UIKit/UIKit.h>
#interface UIView (MyCategory)
- (void)setWidth:(NSInteger)aWidth andHeight:(NSInteger)aHeight;
#end
In UIView + MyCategory.m
#import "UIView + MyCategory.h"
#implementation UIView (MyCategory)
- (void)setWidth:(NSInteger)aWidth andHeight:(NSInteger)aHeight;
{
CGRect frameToUpdate = self.frame;
frameToUpdate.size = CGSizeMake(aWidth, aHeight);
self.frame = frameToUpdate;
}
#end

And to address the actual problem you're trying to solve, I highly recommend using this set of helpers https://github.com/kreeger/BDKGeometry

Following Obj-C style conventions (as selecting the right tools for the job) make it more efficient for others to read and comprehend our code. The Objective-C style here needs a bit of cleanup. See my notes after the source if you're interested. On to a more concise way to do this:
You can go the class method route (perhaps in a view manipulation class)
#implementation CCViewGeometry
+ (void)adjustView:(UIView *)view toSize:(CGSize)size
{
CGRect frame = view.frame;
frame.size = size;
view.frame = frame;
}
#end
or the UIView category route
#implementation UIView (CCGeometry)
- (void)resize:(CGSize)size
{
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
#end
Style notes pertinent to code found on this page:
All method params should begin with a lower-case char.
setFoo: is used in #property synthesis & by convention your method name indicates setting a property named object to the value of object. You're setting the size, not the object itself.
Be explicit. Why have a method called setObject: when you know the general type of object being passed?
Width & height in UIKit are represented (rightly) by CGFloat, not NSInteger. Why pass width + height instead of CGSize anyway?
Try using class methods when state is not required. + is your friend. Don't fire up instances for every little thing (most singleton methods I see in ObjC code should be refactored as class methods).
The programmers that don't care about the little things end up with unmaintainable code—code that will slow them and those that come after them down. Convention and style matter a lot on any decent-sized project.

Related

COCOA: How to create a round button. It should be clickable only in its boundary

I am trying to create a round button which is only clickable in its boundaries.
What I have done
// imported QuartzCore
#import <QuartzCore/QuartzCore.h>
//created an IBOutlet for the button
IBOutlet NSButton* btn;
//defined the button height width (same)
#define ROUND_BUTTON_WIDTH_HEIGHT 150
//set corner radius
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
btn.frame = CGRectMake(0, 0, ROUND_BUTTON_WIDTH_HEIGHT, ROUND_BUTTON_WIDTH_HEIGHT);
btn.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
}
//set view layer to YES
Problem
The button is clickable outside its boundaries.
When I am setting its position and when I am resizing the window it is getting back to its right position (the actual positon of the button is center of the window)
I have also tried to subclass NSButton and assign the class to the button but results are the same.
#import "roundBtn.h"
#import <QuartzCore/QuartzCore.h>
#implementation roundBtn
#define ROUND_BUTTON_WIDTH_HEIGHT 142
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
self.frame = CGRectMake(0, 0, ROUND_BUTTON_WIDTH_HEIGHT, ROUND_BUTTON_WIDTH_HEIGHT);
self.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
}
#end
You can either reimplement the button behavior using a custom NSView and then it will respect the mouseDown events of a masksToBounds layer. NSButton doesn't work this way so the only way to tell your subclassed button that it won't be hit outside of the circle is to override the -hitTest: method to detect hits within the circle:
- (NSView *)hitTest:(NSPoint)aPoint {
NSPoint p = [self convertPoint:aPoint fromView:self.superview];
CGFloat midX = NSMidX(self.bounds);
CGFloat midY = NSMidY(self.bounds);
if (sqrt(pow(p.x-midX, 2) + pow(p.y-midY, 2)) < self.bounds.size.width/2.0) {
return self;
}
return nil;
}
In overriding this, you are telling the button that it will only register a hit if it is within the circle (using a little trigonometry). Returning self indicates the button was hit, returning nil indicates it was not hit.

implement previousScrollViewYOffset not work

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat size = frame.size.height - 21;
CGFloat framePercentageHidden = ((20 - frame.origin.y) / (frame.size.height - 1));
CGFloat scrollOffset = scrollView.contentOffset.y;
CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
if (scrollOffset <= -scrollView.contentInset.top) {
frame.origin.y = 20;
} else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
frame.origin.y = -size;
} else {
frame.origin.y = MIN(20, MAX(-size, frame.origin.y - scrollDiff));
}
[self.navigationController.navigationBar setFrame:frame];
[self updateBarButtonItems:(1 - framePercentageHidden)];
self.previousScrollViewYOffset = scrollOffset;
}
ERROR:
Property 'previousScrollViewYOffset' not found on object of type 'HomeScreen'
#interface HomeScreen : UITableViewController <UIScrollViewDelegate,UIScrollViewAccessibilityDelegate>
I added ScrollViewDelegate why not see previousScrollViewYOffset any idea ?
Declaring that your class conforms to the UIScrollViewDelegate protocol does not give it access to additional properties. It means that your class will implement the methods specified in the delegate protocol, and that your class can therefore support a UIScrollView as its delegate. So, all that declaring your class is a UIScrollViewDelegate will do is enable the method you posted to get called.
Regarding the error you're getting, you should try creating the previousScrollViewYOffset property yourself and initializing it with a value such as 0.
I've never tried to create this specific effect, so I could be off, but it looks like previousScrollViewYOffset is a property on your class that you are calculating each time ScrollViewDidScroll is called. The result of that calculation is then used the next time this method is called. If that's the case, you'll need to create that property yourself, and initialize it in your class's initialization method.
Give this a shot [edited to add code] and let me know how it goes:
In your .h:
#property CGFloat thisFloat;
At the top of your .m:
#synthesize thisFloat;
In your initialization method:
thisFloat = 0.0;
Alternative to making it a property and synthesizing it - because no outside classes will access the property - you could declare it as a private variable at the top of your .m like this:
#implementation HomeScreen {
CGFloat previousScrollViewYOffset;
}

MKMapView hide map tiles and set transparent background

I am trying to display some annotations on a map. I want to use the MKMapView class because of the way it handles annotations, it's great for me. But I have my custom map system which works with its own view. I have tried to implement method swizzling as suggested in this answer: https://stackoverflow.com/a/10702022/1152596 , but I had no luck. The tiles are not being displayed, which is ok, but the background is not transparent, it has a gray-like color. See screenshot below:
The black path is my annotation. Behind is the view with my own map I don't get to see. I'm sure it's behind, if I don't add MKMapView I can see it.
I know what I'm trying here is very hacky, but no alternative came to my mind.
The code for the swizzling from the linked answer is:
I define:
// Import runtime.h to unleash the power of objective C
#import <objc/runtime.h>
// this will hold the old drawLayer:inContext: implementation
static void (*_origDrawLayerInContext)(id, SEL, CALayer*, CGContextRef);
// this will override the drawLayer:inContext: method
static void OverrideDrawLayerInContext(UIView *self, SEL _cmd, CALayer *layer, CGContextRef context)
{
// uncommenting this next line will still perform the old behavior
//_origDrawLayerInContext(self, _cmd, layer, context);
// change colors if needed so that you don't have a black background
layer.backgroundColor = RGB(35, 160, 211).CGColor;
CGContextSetRGBFillColor(context, 35/255.0f, 160/255.0f, 211/255.0f, 1.0f);
CGContextFillRect(context, layer.bounds);
}
In my viewDidLoad method:
UIView* scrollview = [[[[mapView subviews] objectAtIndex:0] subviews] objectAtIndex:0];
UIView* mkTiles = [[scrollview subviews] objectAtIndex:0]; // <- MKMapTileView instance
// Retrieve original method object
Method origMethod = class_getInstanceMethod([mkTiles class],
#selector(drawLayer:inContext:));
// from this method, retrieve its implementation (actual work done)
_origDrawLayerInContext = (void *)method_getImplementation(origMethod);
// override this method with the one you created
if(!class_addMethod([mkTiles class],
#selector(drawLayer:inContext:),
(IMP)OverrideDrawLayerInContext,
method_getTypeEncoding(origMethod)))
{
method_setImplementation(origMethod, (IMP)OverrideDrawLayerInContext);
}

Resize/fit content ONLY HEIGHT of UIWebView?

I'm almost done with resizing my UIWebView. I used the delegate method :
In .h :
#interface myController : UIViewController <UIWebViewDelegate>
In .m :
#pragma mark UIWebView delegate methods
- (void) webViewDidFinishLoad : (UIWebView *) aWebView
{
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;
}
But what I need is a webview with fixed-width and variable-height depending on the content.
The previous code is almost fine but the webview is also extended and scrollable horizontally.
I also tried this :
[myWebView setUserInteractionEnabled:NO];
But it only disables the scroll, not adapts the content.
How to fix this please ?
This might help. I got it working quite easily:
https://stackoverflow.com/a/6104537/1413088

Cocoa: Subclassing NSProgressIndicator on Lion

I have a problem that is driving me crazy. I want to subclass NSProgressIndicator in its "Bar" form to change the color of the progress bar based on a couple of logic states. For that i basically override the -drawRect: as usual. However a very strange thing happens on Lion. Even if my -drawRect just calls the superclass' implementation via [super drawRect:] and does nothing else at all the whole progress bar will be displayed using the style that was used before Lion. If you remember, with Lion the progress bar style has changes to a more flat and sleek look from the prior glassy one.
Again, just overriding -drawRect to do nothing but to call the superclass' implementation changes the style of the progress bar to Leopard's. Does anyone have a slightest idea of what is going on and how i can fix this?
How about making the progress indicator layer-backed and applying a Core Image filter to recolour it?
CustomeProgressIndicator.h:
#import <Cocoa/Cocoa.h>
#interface CustomProgressIndicator : NSView {
double m_minValue;
double m_maxValue;
double m_value;
}
- (void)setMaxValue:(double)newMaxValue;
- (void)setMinValue:(double)newMinValue;
- (void)setDoubleValue:(double)newDoubleValue;
- (void)drawRect:(NSRect)dirtyRect;
#end
CustomProgressIndicator.m:
#import "CustomProgressIndicator.h"
#implementation CustomProgressIndicator
- (void)setMaxValue:(double)newMaxValue
{
m_maxValue = newMaxValue;
}
- (void)setMinValue:(double)newMinValue
{
m_minValue = newMinValue;
}
- (void)setDoubleValue:(double)newDoubleValue
{
m_value = newDoubleValue;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor grayColor] set];
[NSBezierPath fillRect:dirtyRect];
double width = ((m_value - m_minValue) / (m_maxValue - m_minValue));
CGRect bar = dirtyRect;
bar.size.width *= width;
[[NSColor darkGrayColor] set];
[NSBezierPath fillRect:bar];
}