I want to change the default font for all UITextViews. It seems that the easiest way to do this is via custom category. I found this solution: Change the default systemFont used by controls and tried to implement it.
But my UITextViews are added programmatically so the awakeFromNib function is not called. I tried to move it to initWithFrame like this:
-(id)initWithFrame:(CGRect)frame
{
id result = [super initWithFrame:frame];
if (result) {
float size = [self.font pointSize];
NSString *stringfontstyle=self.font.fontName;
if([stringfontstyle rangeOfString:#"Bold"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Black" size:size];
}
else if ([stringfontstyle rangeOfString:#"Italic"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Oblique" size:size];
}
else if ([stringfontstyle rangeOfString:#"Medium"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Medium" size:size];
}
else {
self.font = [UIFont fontWithName:#"Avenir-Roman" size:size];
}
}
return result;
}
Weird is that if my category contains initWithFrame function, the UITextView disappears. What is it that I'm missing?
Note: I'm using autoLayout so the initWithFrame is called with CGRectZero, but I suppose that isn't the problem.
EDIT:
The problem is that the font is null when the UITextView is initiated. So what method would be appropriate to place the code into?
when category contains a method, it overrides the class's method... and thats not good. subclassing would work.. method swizzling might be a way but...
why don't you just subclass UITextView - then you can keep your initWithFrame thingy or maybe override font
- (UIFont*)font {
if(!myFont) {
_myFont = xy;
}
id superFont = super.font;
if(![superFont.name isEqualTo:_myFont.name]) {
super.font = [myFont fontWithSize:superFont.pointSize];
}
return _myFont;
}
or setFont:
- (void)setFont:(UIFont*)newFont {
if(!myFont) {
_myFont = xy;
}
id thisFont = [_myFont fontWithSize:newFont.pointSize];
super.font = thisFont;
Related
I use bindings to NSObjectController within XIB. When I set new content object of NSObjectController the only textfield value which doesn't change is the one that has first responder. Model changes without an issue.
If I don't use custom getter/setter the textfield that has firstResponder (isBeingEdited) changes without an issue.
What's wrong with my KVC, KVO?
My custom getter/setter is below pic.
PS: I don't want to make window a first responder before I change content object to make it work.
static const CGFloat MMsInOneInch = 25.4;
static const CGFloat inchesInOneMM = 0.0393700787402;
- (void)setPaperWidth:(CGFloat)paperWidth
{
[self willChange];
CGFloat newWidth = paperWidth * [self conversionKoeficientToDefaultUnitType];
if (!_isChangingPaperSize) {
if (self.paperSize == PPPaperSizeA4 && fabs(newWidth - widthSizeOfA4) > 0.001) {
[self setPaperSize:PPPaperSizeCustom];
}
if (self.paperSize == PPPaperSizeUSLetter && fabs(newWidth - widthSizeOfUSLetter) > 0.001 ) {
[self setPaperSize:PPPaperSizeCustom];
}
}
[self willChangeValueForKey:#"paperWidth"];
_paperWidth = newWidth;
[self didChangeValueForKey:#"paperWidth"];
[self didChange];
}
- (CGFloat)conversionKoeficientToDefaultUnitType
{
if ([self defaultUnitType] == [self unitType]) {
return 1;
}
if ([self defaultUnitType] == PPPrintSettingsUnitTypeMM) {
return MMsInOneInch;
}
if ([self defaultUnitType] == PPPrintSettingsUnitTypeInch) {
return inchesInOneMM;
}
return 1;
}
- (CGFloat)paperWidth
{
return _paperWidth / [self conversionKoeficientToDefaultUnitType];
}
I forgot that I use NSNumberFormatter with min/max value which where blocking NSTextField to update.
Can someone tell me if I have translated the first 2 lines correctly to Swift and if the first part is correctly? Also, could anyone help me figure out the rest. I can't figure out how to translate the if statement at the bottom..
[C addTarget:self action:#selector(outsideOfKey: forEvent:) forControlEvents:UIControlEventTouchDragOutside|UIControlEventTouchDragInside];
[C addTarget:self action:#selector(keyGetsLeft: forEvent:) forControlEvents:UIControlEventTouchUpOutside | UIControlEventTouchUpInside];
-(void) outsideOfKey:(id)sender forEvent:(UIEvent *)event
{
for(UITouch *t in [event allTouches])
{
CGPoint touchPoint = [t locationInView:window];
if(CGRectContainsPoint(C.frame, touchPoint))
{
C.highlighted = YES;
}
else{
C.highlighted = NO;
}
Translated to swift
C.addTarget(self, action:Selector("outsideOfKey:forEvent:"), forControlEvents:.TouchDragOutside)
C.addTarget(self, action:Selector("outsideOfKey:forEvent:"), forControlEvents:.TouchDragInside)
C.addTarget(self, action:Selector("keyGetsLeft:forEvent:"), forControlEvents:.TouchUpOutside)
C.addTarget(self, action:Selector("keyGetsLeft:forEvent:"), forControlEvents:.TouchUpInside)
func outsideOfKey (sender: AnyObject, forEvent: UIEvent) {
let touch = event.allTouches() as? UITouch
for touch
{
var touchPoint : CGPoint = touch.locationInView(window)
if(CGRectContainsPoint(C.frame, touchPoint))
{
C.highlighted = YES;
}
else{
C.highlighted = NO;
}
}
Try something like:
C.addTarget(self, action:Selector("outsideOfKey:forEvent:"), forControlEvents:.TouchDragOutside | .TouchDragInside)
C.addTarget(self, action:Selector("keyGetsLeft:forEvent:"), forControlEvents:.TouchUpOutside | .TouchUpInside)
func outsideOfKey(sender: AnyObject, forEvent event: UIEvent) {
if let touches = event.allTouches()?.allObjects as? [UITouch] {
for touch in touches {
var touchPoint : CGPoint = touch.locationInView(window)
if CGRectContainsPoint(C.frame, touchPoint) == true {
C.highlighted = true;
} else {
C.highlighted = false;
}
}
}
}
Points:
You can still use the "|" operator on enums in swift (most of the time)
You don't put parentheses around the conditional clause in swift
YES and NO are not valid in swift, you much use true and false
Swift does not support sets so it is easiest to get the touches as an Array so you can iterate using a simple swift for...in loop
The "if let ..." pattern is called optional binding, if the right hand side is not nil "touches" will be set to the value and the code in the following braces will be executed, otherwise it will skip the block of code
The way "?" is used after ".allTouches()" is called optional chaining, if .allTouches() returns nil, the whole expression will return nil
Hope this is of some use to you, let me know if you have any more swift queries!
How do I make a CALayer accessible? Specifically, I want the layer to be able to change its label on the fly, since it can change at any time. The official documentation's sample code does not really allow for this.
The following assumes that you have a superview whose layers are all of class AccessableLayer, but if you have a more complex layout this scheme can be modified to handle that.
In order to make a CALayer accessible, you need a parent view that implements the UIAccessibilityContainer methods. Here is one suggested way to do this.
First, have each layer own its UIAccessibilityElement
#interface AccessableLayer : CALayer
#property (nonatomic) UIAccessibilityElement *accessibilityElement;
#end
now in its implementation, you modify the element whenever it changes:
#implementation AccessableLayer
... self.accessibilityElement.accessibilityLabel = text;
#end
The AccessableLayer never creates the UIAccessibilityElement, because the constructor requires a UIAccessibilityContainer. So have the super view create and assign it:
#pragma mark - accessibility
// The container itself is not accessible, so return NO
- (BOOL)isAccessibilityElement
{
return NO;
}
// The following methods are implementations of UIAccessibilityContainer protocol methods.
- (NSInteger)accessibilityElementCount
{
return [self.layer.sublayers count];
}
- (id)accessibilityElementAtIndex:(NSInteger)index
{
AccessableLayer *panel = [self.layer.sublayers objectAtIndex:index];
UIAccessibilityElement *element = panel.accessibilityElement;
if (element == nil) {
element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:self];
element.accessibilityFrame = [self convertRect:panel.frame toView:[UIApplication sharedApplication].keyWindow];
element.accessibilityTraits = UIAccessibilityTraitButton;
element.accessibilityHint = #"some hint";
element.accessibilityLabel = #"some text";
panel.accessibilityElement = element;
}
return element;
}
- (NSInteger)indexOfAccessibilityElement:(id)element
{
int numElements = [self accessibilityElementCount];
for (int i = 0; i < numElements; i++) {
if (element == [self accessibilityElementAtIndex:i]) {
return i;
}
}
return NSNotFound;
}
I ran into this issue twice now, and am curious to know if there is a correct way of getting the following example to work. I know there are other ways /workarounds for doing the same thing but I am wondering why the compiler doesn't recognise my casting and if I am missing something obvious here.
Suppose I have two table views with different style header views that I need to provide. SectionHeaderViewA is a UIView subclass with a custom property textLabelA, SectionHeaderViewB is also a UIView subclass with a custom property textLabelB.
In the method:
- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
id headerView;
if (tableView.tag == TAG_A)
{
headerView = (SectionHeaderViewA*)[[SectionHeaderViewA alloc] init];
headerView.textLabelA = ... // I am unable to access the custom property here even after casting (SectionHeaderViewA*) above.
} else if (tableView.tag == TAG_B) {
headerView = (SectionHeaderViewB*)[[SectionHeaderViewB alloc] init];
headerView.textLabelB = ... // Same as above, even after casting the custom property is not recognised
}
return headerView;
}
Even after casting (SectionHeaderViewA*) and (SectionHeaderViewB*) to my headerView ivar, I am still unable to access their respective custom properties. It's like the compiler is still seeing headerView as an unknown / id type but why?
The cast is not in the correct place. Cast headerView before sending the appropriate textLabel A or B message.
id headerView;
if (tableView.tag == TAG_A)
{
headerView = [[SectionHeaderViewA alloc] init];
((SectionHeaderViewA*)headerView).textLabelA = ... // I am unable to access the custom property here even after casting (SectionHeaderViewA*) above.
} else if (tableView.tag == TAG_B) {
headerView = [[SectionHeaderViewB alloc] init];
((SectionHeaderViewB*)headerView).textLabelB = ... // Same as above, even after casting the custom property is not recognized
}
return headerView;
Once you move the cast, you will be able to send the correct message.
the type of headerView is "id", which means it doesn't know about your extra properties etc (the cast doesn't change the type of "headerView").
you could do something like:
if (tableView.tag == TAG_A)
{
SectionHeaderViewA* headerView = [[SectionHeaderViewA alloc] init];
headerView.textLabelA = ...
return headerView;
} else if (tableView.tag == TAG_B) {
SectionHeaderViewB* headerView = [[SectionHeaderViewB alloc] init];
headerView.textLabelB = ...
return headerView;
}
return nil;
Your cast is not doing anything as you are casting into id.
Whilst #sean's answer works and it does the single exit it is pretty ugly having all the curly brackets I would probably go for
id headerView = nil; // Initialize to nil... you may not go into either side of your if
if (TAG_A == tableView.tag) {
SectionHeaderViewA *sectionHeaderViewA = [[SectionHeaderViewA alloc] init];
sectionHeaderViewA.textLabelA = ...
headerView = sectionHeaderViewA;
} else if (TAG_B == tableView.tag) {
SectionHeaderViewB *sectionHeaderViewB = [[SectionHeaderViewB alloc] init];
sectionHeaderViewB.textLabelB = ...
headerView = sectionHeaderViewB;
}
return headerView;
Or another possibility (potentially over engineering the problem) is make both sectionHeaderViewA and sectionHeaderViewB conform to a protocol and then you can make it a little tidier still.
SectionHeaderInterface.h
#protocol SectionHeaderInterface <NSObject>
#property (strong, nonatomic) UILabel *textLabel;
#end
SectionHeaderView(A|B).h
#import "SectionHeaderInterface.h"
#interface SectionHeaderView(A|B) : UIView <SectionHeaderInterface>
// ... rest of interface
#end
SectionHeaderView(A|B).m
#implementation SectionHeaderView(A|B)
#synthesize textLabel = _textLabel;
// ... rest of your class
#end
YourController.m
id<SectionHeaderInterface> headerView = nil; // Initialize to nil... you may not go into either side of your if
if (TAG_A == tableView.tag) {
headerView = [[SectionHeaderViewA alloc] init];
} else if (TAG_B == tableView.tag) {
headerView = [[SectionHeaderViewB alloc] init];
}
headerView.textLabel.text = ...
return headerView;
I want to get the name of a sender in Objective-C. For example, below I have a method which is called by an instance of UISlider in Interface Builder, I want to know what the instance name of it is so I can later add conditional blocks to the method for which instance of UISlider called the method.
e.g.
-(IBAction)sliderChanged:(UISlider *)sender {
//labAt1TimeRequired.text = [NSString stringWithFormat:#"%.1f", [sender value]];
NSLog(#"%#",sender);
Outputs:2010-10-15 22:46:02.257 EPC[3225:207] <UISlider: 0x495b140; frame = (205 3; 118 23); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x492e340>>
I want to be able to say
if(sender==myInstanceName) {
//do this
}
You could use
.tag member
to read an write and integer ID for the slider like this:
-(IBAction)sliderChanged:(UISlider
*)sender {
switch (sender.tag) {
case 0:
//SLider 0
break;
case 1:
//SLider 1
break;
default:
break;
}
}
Tag ID's can also be set for components in IB.
If your set on a string then you would need to subclass a UISlider.
You would use the tag property of UIView for identifying the sender.
-(IBAction)sliderChanged:(UISlider *)sender {
//labAt1TimeRequired.text = [NSString stringWithFormat:#"%.1f", [sender value]];
if (sender.tag == 1)
{
// do whatever
}
else
{
// do something else
}
}