Related
I’m using a custom font in a UITextField, which has secureTextEntry turned on. When I’m typing in the cell, I see the bullets in my chosen font, but when the field loses focus, those bullets revert to the system standard font. If I tap the field again, they change back to my font, and so on.
Is there a way I can ensure that they continue to display the custom font’s bullets, even when the field is out of focus?
A subclass that works this issue around. Create an arbitrary UITextField, then set the secure property to YES (via KVC in IB).
Actually it implements a comment suggested by lukech. When textfield ends editing, it switches to an arbitrary textfield, then set a bulk of dots into, and some hack in text accessor to always get the actual text the field holds.
#interface SecureTextFieldWithCustomFont : UITextField
#property (nonatomic) BOOL secure;
#property (nonatomic, strong) NSString *actualText;
#end
#implementation SecureTextFieldWithCustomFont
-(void)awakeFromNib
{
[super awakeFromNib];
if (self.secureTextEntry)
{
// Listen for changes.
[self addTarget:self action:#selector(editingDidBegin) forControlEvents:UIControlEventEditingDidBegin];
[self addTarget:self action:#selector(editingDidChange) forControlEvents:UIControlEventEditingChanged];
[self addTarget:self action:#selector(editingDidFinish) forControlEvents:UIControlEventEditingDidEnd];
}
}
-(NSString*)text
{
if (self.editing || self.secure == NO)
{ return [super text]; }
else
{ return self.actualText; }
}
-(void)editingDidBegin
{
self.secureTextEntry = YES;
self.text = self.actualText;
}
-(void)editingDidChange
{ self.actualText = self.text; }
-(void)editingDidFinish
{
self.secureTextEntry = NO;
self.actualText = self.text;
self.text = [self dotPlaceholder];
}
-(NSString*)dotPlaceholder
{
int index = 0;
NSMutableString *dots = #"".mutableCopy;
while (index < self.text.length)
{ [dots appendString:#"•"]; index++; }
return dots;
}
#end
May be augmented to work with non NIB instantiations, handling default values, etc, but you probably get the idea.
For those having trouble with losing custom fonts when toggling secureTextEntry, I found a work-around (I'm using the iOS 8.4 SDK). I was trying to make a toggle for showing/hiding a password in a UITextField. Every time I'd toggle secureTextEntry = NO my custom font got borked, and only the last character showed the correct font. Something funky is definitely going on with this, but here's my solution:
-(void)showPassword {
[self.textField resignFirstResponder];
self.textField.secureTextEntry = NO;
}
First responder needs to be resigned for some reason. You don't seem to need to resign the first responder when setting secureTextEntry to YES, only when setting to NO.
The actual problem appears to be that the editing view (UITextField does not draw its own text while editing) uses bullets (U+2022) to draw redacted characters, while UITextField uses black circles (U+25CF). I suppose that in the default fonts, these characters look the same.
Here's an alternate workaround for anyone interested, which uses a custom text field subclass, but doesn't require juggling the text property or other special configuration. IMO, this keeps things relatively clean.
#interface MyTextField : UITextField
#end
#implementation MyTextField
- (void)drawTextInRect:(CGRect)rect
{
if (self.isSecureTextEntry)
{
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = self.textAlignment;
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
[attributes setValue:self.font forKey:NSFontAttributeName];
[attributes setValue:self.textColor forKey:NSForegroundColorAttributeName];
[attributes setValue:paragraphStyle forKey:NSParagraphStyleAttributeName];
CGSize textSize = [self.text sizeWithAttributes:attributes];
rect = CGRectInset(rect, 0, (CGRectGetHeight(rect) - textSize.height) * 0.5);
rect.origin.y = floorf(rect.origin.y);
NSMutableString *redactedText = [NSMutableString new];
while (redactedText.length < self.text.length)
{
[redactedText appendString:#"\u2022"];
}
[redactedText drawInRect:rect withAttributes:attributes];
}
else
{
[super drawTextInRect:rect];
}
}
#end
While this is an iOS bug (and new in iOS 7, I should add), I do have another way to work around it that one might find acceptable. The functionality is still slightly degraded but not by much.
Basically, the idea is to set the font to the default font family/style whenever the field has something entered in it; but when nothing is entered, set it to your custom font. (The font size can be left alone, as it's the family/style, not the size, that is buggy.) Trap every change of the field's value and set the font accordingly at that time. Then the faint "hint" text when nothing is entered has the font that you want (custom); but when anything is entered (whether you are editing or not) will use default (Helvetica). Since bullets are bullets, this should look fine.
The one downside is that the characters, as you type before being replaced by bullets, will use default font (Helvetica). That's only for a split second per character though. If that is acceptable, then this solution works.
I found a trick for this issue.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if ([textField tag]== TAG_PASS || [textField tag]== TAG_CPASS)
{
// To fix password dot size
if ([[textField text] isEqualToString:#"" ])
{
[textField setText:#" "];
[textField resignFirstResponder];
[textField becomeFirstResponder];
[textField setText:#""];
}
}
}
[passWordTextField resignFirstResponder];
passWordTextField.secureTextEntry = !passWordTextField.secureTextEntry;
[passWordTextField becomeFirstResponder];
This is the fastest way to solve this bug!
iOS is acting a bit strange when it comes to custom fonts. Try removing "Adjust to Fit" for that textfield. If that doesn't work, I'm guessing that what bothering you is the size increase of the font.
A simple solution for that would be:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if(textField.secureTextEntry)
{
[textField setFont:[UIFont fontWithName:#"Helvetica-Bold" size:10.0]];
}
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
if(textField.secureTextEntry)
{
[textField setFont:[UIFont fontWithName:#"Helvetica-Bold" size:10.0]];
}
}
You'll need to play with the size a bit in order for it to look like there is no size change when loosing focus on the UITextField.
If you have a major spacing problem between characters like in the edited question, the simplest (and a bit ugly) solution would be to create a Bullet image that matches the above size & spacing and matches the amount of characters entered by the user that will appear when the user leaves the UITextField.
A secureTextEntry text field can be avoided altogether:
NSString *pin = #"";
BOOL pasting = FALSE;
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(!pasting) {
pin = [pin stringByReplacingCharactersInRange:range withString:string];
// Bail out when deleting a character
if([string length] == 0) {
return YES;
}
pasting = TRUE;
[textField paste:#"●"];
return NO;
} else {
pasting = FALSE;
return YES;
}
}
I recommend to resignFirstResponder before you change scureTextEntry and then becomeFirstResponder again as it is posted here: https://stackoverflow.com/a/34777286/1151916
Swift 5 and iOS 14 is around but isSecureTextEntry set to true for custom font still displays the wrong size bullets, although the actual leading letter is of the correct size.
None of the solutions from stack overflow has worked for me except a hacky workaround of setting the font to the system font when password is in secure mode.
if textField.isSecureTextEntry {
self.textField.font = UIFont.systemFont(ofSize: 17)
} else {
self.textField.font = UIFont(name: "Roboto-Regular", size: 17)
}
I have a subclass of UITextField and I'm trying ot add an opaque subview to it. However, when I type in the UITextField, it appears transparent and I can see the text behind the view. How can I make the view fully opaque? Here is the code that inits the subclass and adds the view.
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
_dropdownIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"ab- dropdown-on"]];
dropdownIcon.backgroundColor = [UIColor blackColor];
_dropdownIcon.frame = CGRectMake(self.frame.size.width-DROPDOWN_ICON_SIZE,0,DROPDOWN_ICON_SIZE,DROPDOWN_ICON_SIZE);
[self addSubview:_dropdownIcon];
}
return self;
}
Your subclass needs to override the -textRectForBounds: method to return the actual area you want text to draw in, e.g.
- (CGRect)textRectForBounds:(CGRect)bounds
{
CGRect textRect = [super textRectForBounds:bounds];
textRect.size.width -= 30; // or however wide your control is—play with this value
return textRect;
}
What you’re seeing is not the icon being non-opaque—the text is just being drawn over it, because the text field doesn’t “know” the icon is there.
You might also look into the rightView property and the related -rightViewRectForBounds: method, though those are mostly useful if you want the text field to automatically show and hide your accessory view.
I have a bunch of buttons on the screen which are positioned intuitively visually but are not read in an intuitive order by VoiceOver. This is because certain buttons like Up and Down are placed above and below each other. However, voiceover starts reading from Left to Right, from Top to Bottom, it seems.
This results in voiceover reading the button to the right of "Up" after "Up", instead of reading "Down" immediately afterward.
How do I force voiceover to read the button that I want to read? I should mention that I'm using the swipe-to-cycle-through-elements feature on voiceover.
All my buttons are subclassed versions of UIView and UIButton. Here's an example of a button initiator I use. Ignore the pixel count - I know that's bad form but I'm in a pinch at the moment:
UIButton* createSpecialButton(CGRect frame,
NSString* imageName,
NSString* activeImageName,
id target,
SEL obClickHandler)
{
UIButton* b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setImage:[GlobalHelper nonCachedImage:imageName ofType:#"png"]
forState:UIControlStateNormal];
[b setImage:[GlobalHelper nonCachedImage:activeImageName ofType:#"png"]
forState:UIControlStateHighlighted];
[b addTarget:target action:obClickHandler forControlEvents:UIControlEventTouchUpInside];
b.frame= frame;
return b;
}
- (UIButton *) createSendButton {
CGFloat yMarker = 295;
UIButton* b = createSpecialButton(CGRectMake(160, yMarker, 70, 45),
#"Share_Btn",
#"Share_Selected_Btn",
self,
#selector(sendAction));
b.accessibilityHint = #"Send it!";
b.accessibilityLabel = #"Stuff for voiceover to be added";
[self.view addSubview:b];
return b;
}
You can change the order by setting the view's accessibilityElements array:
self.view.accessibilityElements = #[self.view1, self.view2, self.view3, self.view4];
or
self.anotherView.accessibilityElements = #[self.label1, self.txtView1, self.label2, self.txtView2];
If you need to enable user interaction programmatically:
[self.view1 setUserInteractionEnabled:YES];
Note: If the view is hidden, VoiceOver will not pass through it.
The easiest answer to this lies in creating a UIView subclass that contains your buttons, and responds differently to the accessibility calls from the system. These important calls are:
-(NSInteger)accessibilityElementCount
-(id)accessibilityElementAtIndex:
-(NSInteger)indexOfAccessibilityElement:
I've seen a few of these questions, and answered one before, but I've not seen a generic example of how to reorder the VoiceOver focus. So here is an example of how to create a UIView subclass that exposes its accessible subviews to VoiceOver by tag.
AccessibilitySubviewsOrderedByTag.h
#import <UIKit/UIKit.h>
#interface AccessibilitySubviewsOrderedByTag : UIView
#end
AccessibilitySubviewsOrderedByTag.m
#import "AccessibilityDirectional.h"
#implementation AccessibilitySubviewsOrderedByTag {
NSMutableArray *_accessibilityElements;
}
//Lazy loading accessor, avoids instantiating in initWithCoder, initWithFrame, or init.
-(NSMutableArray *)accessibilityElements{
if (!_accessibilityElements){
_accessibilityElements = [[NSMutableArray alloc] init];
}
return _accessibilityElements;
}
// Required accessibility methods...
-(BOOL)isAccessibilityElement{
return NO;
}
-(NSInteger)accessibilityElementCount{
return [self accessibilityElements].count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index{
return [[self accessibilityElements] objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element{
return [[self accessibilityElements] indexOfObject:element];
}
// Handle added and removed subviews...
-(void)didAddSubview:(UIView *)subview{
[super didAddSubview:subview];
if ([subview isAccessibilityElement]){
// if the new subview is an accessibility element add it to the array and then sort the array.
NSMutableArray *accessibilityElements = [self accessibilityElements];
[accessibilityElements addObject:subview];
[accessibilityElements sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
// Here we'll sort using the tag, but really any sort is possible.
NSInteger one = [(UIView *)obj1 tag];
NSInteger two = [(UIView *)obj2 tag];
if (one < two) return NSOrderedAscending;
if (one > two) return NSOrderedDescending;
return NSOrderedSame;
}];
}
}
-(void)willRemoveSubview:(UIView *)subview{
[super willRemoveSubview:subview];
// Clean up the array. No check since removeObject: is a safe call.
[[self accessibilityElements] removeObject:subview];
}
#end
Now simply enclose your buttons in an instance of this view, and set the tag property on your buttons to be essentially the focus order.
In Swift you just have to set view's accessiblityElements array property:
view.accessibilityElements = [view1, view2, view3] // order you wish to have
I know this is an old thread, but I found that the easiest way to do it is to subclass UIView. Then simply modify your main UIView type in storyboard to AccessibiltySubviewsOrderedByTag and update the tags in each subview you want to read in order.
class AccessibilitySubviewsOrderedByTag: UIView {
override func layoutSubviews() {
self.accessibilityElements = [UIView]()
for accessibilitySubview in self.subviews {
if accessibilitySubview.isAccessibilityElement {
self.accessibilityElements?.append(accessibilitySubview)
}
}
self.accessibilityElements?.sort(by: {($0 as AnyObject).tag < ($1 as AnyObject).tag})
}
}
This doesn’t directly answer the original question, but it answers the title of the question:
When I want VoiceOver to swipe down a column, I have been using a containing view for the column with shouldGroupAccessibilityChildren set.
I wish I had known this earlier, because it can be a pain to retroactively insert containers into an autolayout situation…
I tried Wesley's answer of setting the array of the accessibilityElements but it didn't work for me.
Apple has some documentation Enhancing the Accessibility of Table View Cells with an example in code. Basically you set the accessibility label of the cell (the parent view) to the values of the accessibility labels of the child views.
[cell setAccessibilityLabel:[NSString stringWithFormat:#"%#, %#", cityLabel, temperatureLabel]];
This is what worked for me.
I found a convenience way yesterday. Similar to #TejAces ' answer.
Make a new swift file, then copy these things into it.
import UIKit
extension UIView {
func updateOrder(_ direction: Bool = true) {
var tempElements: [Any]? = [Any]()
let views = (direction) ? subviews : subviews.reversed()
for aView in views {
tempElements?.append(aView)
}
accessibilityElements = tempElements
}
}
class ReorderAccessibilityByStoryBoardView: UIView {
override func didAddSubview(_ subview: UIView) {
updateOrder()
super.didAddSubview(subview)
}
}
Set the UIView(contains views you want to reorder)'s class as ReorderAccessibilityByStoryBoardView. Then you can reorder them by reordering storyboard's view list.
Because subview doesn't contain views in StackView/ScrollView, you need to make a independent class in this file. Such as the ReorderAccessibilityByStoryBoardStackView down below.
class ReorderAccessibilityByStoryBoardStackView: UIStackView {
override func didAddSubview(_ subview: UIView) {
updateOrder(false)
super.didAddSubview(subview)
}
}
With these codes, you can also reorder view's added in code by adding them in a specific order.
I think you can do it in the storyboard. The VoiceOver order is determined by the order of the views in the document outline.
Just drag and drop the views in the view hierarchy in the right order.
Edit:
Sorry I can not post screenhots until 10 reputation. In the storyboard, the document outline is the area on the left where your scenes with their subviews are listed. Here, subviews are ordered one below each other. When you change this order, the reading-order of VoiceOver will change.
Swift 5.x
Following the advice of ChrisJF , I've wrote a little extension to bypass the Apple bug around the correct order reading items.
extension UIView {
func setAccessibilityOrder(_ arrayViews:[Any]?){
self.accessibilityElements = arrayViews
let arrayStrings:[String] = arrayViews?.map { String(($0 as AnyObject).accessibilityLabel ?? "") } ?? []
let formatList = arrayStrings.map { _ in "%#" }.joined(separator: ", ")
self.accessibilityLabel = String(format: formatList, arguments:arrayStrings)
self.isAccessibilityElement = true
}
}
Usage:
view1.accessibilityLabel = "my view 1"
label2.accessibilityLabel = "my label 2"
button3.accessibilityLabel = "my button 3"
let order = [view1, label2, button3]
self.setAccessibilityOrder(order) // or self.view.setAccessibilityOrder(order) if you are on a parent controller
Disclaimer: I've been working too late. But, I'm determined to get through this one tonight.
I have an app where I support different color themes. The dark cell backgrounds have been problematic.
I've been poking around trying to find a formidable way to draw the accessory disclosure icon in uitableviewcells with black backgrounds.
I decided to try overriding setAccessoryType to inherit the functionality for my 50+ views:
-(void) addWhiteDisclosureImage {
UIImageView *disclosureView = (UIImageView*) [self.contentView viewWithTag:kDisclosureReplacementImageTag];
if(!disclosureView) {
[super setAccessoryType:UITableViewCellAccessoryNone];
disclosureView = [[UIImageView alloc] initWithImage:self.whiteDisclosureImage];
disclosureView.tag = kDisclosureReplacementImageTag;
disclosureView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
DebugLog(#"%f, %f", self.frame.size.width, self.frame.size.height);
[self.contentView addSubview:disclosureView];
[self.contentView bringSubviewToFront:disclosureView];
[disclosureView release];
}
}
- (void)setAccessoryType:(UITableViewCellAccessoryType)accessoryType {
if(accessoryType == UITableViewCellAccessoryDisclosureIndicator) {
if ([self.viewController isKindOfClass:[ViewControllerBase class]]) {
ViewControllerBase *view = (ViewControllerBase*) self.viewController;
if(view.colorTheme && view.colorTheme.controlBackgroundColor) {
if([ViewColors colorAverage:view.colorTheme.controlBackgroundColor] < 0.2) { //substitute white disclosure indicator
[self addWhiteDisclosureImage];
return;
} else { //not dark enough
[self removeWhiteDisclosureImage];
[super setAccessoryType:accessoryType];
return;
}
} else { //no colorTheme.backgroundColor
[self removeWhiteDisclosureImage];
[super setAccessoryType:accessoryType];
return;
}
} else { //viewController is not type ViewControllerBase
[self removeWhiteDisclosureImage];
[super setAccessoryType:accessoryType];
return;
}
}
UIView *disclosureView = [self.contentView viewWithTag:kDisclosureReplacementImageTag];
if(disclosureView)
[disclosureView removeFromSuperview];
[super setAccessoryType:accessoryType];
}
This override is typically called in cellForRowAtIndexPath.
It seemed like a good option until I drill down and come back. For some cells, the cell frame will be a great deal larger than the first time through. This consistently happens to the same cell in a list of 6 that I've been testing against. There's clearly something unique about this cell: it's frame.size.
Here is the size of the cell that I log for the first tableview load (in some cases every load/reload):
320.000000, 44.000000
This is the difference in what I get for some (not all) of the cells after call to reloadData:
759.000000, 44.000000
Does anyone know why this might happen?
Update: the suspect cell's custom accessory disclosure view almost acts like it's autoresizing flag is set to none. I confirmed this by setting all to none. I say almost because I see it line up where it should be after reloadData. A split second later it moves clear over to the left (where they all end up when I opt for no autoresizing).
Don't mess around with subviews and calculating frames.
Just replace the accessoryView with the new imageView. Let iOS do the work.
I am using a UITextField with a UIPickerView for its inputView, so that when the user taps the text field, a picker is summoned for them to select an option from.
Nearly everything works, but I have one problem: the cursor still flashes in the text field when it is active, which is ugly and inappropriate, since the user is not expected to type into the field and is not presented with a keyboard. I know I could hackily solve this by setting editing to NO on the text field and tracking touches on it, or by replacing it with a custom-styled button, and summoning the picker via code. However, I want to use the UITextFieldDelegate methods for all the event handling on the text field and hacks such as replacing the text field with a button do not permit this approach.
How can I simply hide the cursor on the UITextField instead?
Simply subclass UITextField and override caretRectForPosition
- (CGRect)caretRectForPosition:(UITextPosition *)position
{
return CGRectZero;
}
As of iOS 7 you can now just set the tintColor = [UIColor clearColor] on the textField and the caret will disappear.
You can just clear the textfield's tintColor
self.textField.tintColor = [UIColor clearColor];
Swift 3.0
self.textField.tintColor = .clear
You might also want to stop the user from selecting, copying or pasting any text so that the only text input comes from the picker view.
- (CGRect) caretRectForPosition:(UITextPosition*) position
{
return CGRectZero;
}
- (NSArray *)selectionRectsForRange:(UITextRange *)range
{
return nil;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(copy:) || action == #selector(selectAll:) || action == #selector(paste:))
{
returnNO;
}
return [super canPerformAction:action withSender:sender];
}
http://b2cloud.com.au/tutorial/disabling-the-caret-and-text-entry-in-uitextfields/
Check out the property selectedTextRange of the protocol UITextInput, to which the class UITextField conforms. Few! That's a lesson in object-oriented programing right there.
Hide Caret
To hide the caret, nil out the text field's selected text range.
textField.selectedTextRange = nil; // hides caret
Unhide Caret
Here are two ways to unhide the caret.
Set the text field's selected text range to the end of the document.
UITextPosition *end = textField.endOfDocument;
textField.selectedTextRange = [textField textRangeFromPosition:end
toPosition:end];
To keep the caret in the same spot, first, store the text field's selected text range to an instance variable.
_textFieldSelectedTextRange = textField.selectedTextRange;
textField.selectedTextRange = nil; // hides caret
Then, when you want to unhide the caret, simply set the text field's selected text range back to what it was originally:
textField.selectedTextRange = _textFieldSelectedTextRange;
_textFieldLastSelectedTextRange = nil;
Swift 5 version of Net's post
override func caretRect(for position: UITextPosition) -> CGRect {
return .zero
}
override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
return []
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
Answer provided by the OP, copied from the question body to help clean up the ever growing tail of unanswered questions.
I found another solution: subclass UIButton and override these methods
- (UIView *)inputView {
return inputView_;
}
- (void)setInputView:(UIView *)anInputView {
if (inputView_ != anInputView) {
[inputView_ release];
inputView_ = [anInputView retain];
}
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
Now the button, as a UIResponder, have a similar behavior than UITextField and an implementation pretty straightforward.
set the tintColor to Clear Color
textfield.tintColor = [UIColor clearColor];
and you can also set from the interface builder
If you want to hide cursor, you can easily use this! It worked for me..
[[textField valueForKey:#"textInputTraits"] setValue:[UIColor clearColor] forKey:#"insertionPointColor"]
Answer provided by the OP, copied from the question body to help clean up the ever growing tail of unanswered questions.
I think I have the correct solution but If it can be improved will be welcome :) Well, I made a subclass of UITextField and overriden the method that returns the CGRect for the bounds
-(CGRect)textRectForBounds:(CGRect)bounds {
return CGRectZero;
}
The problem? The text doesn't show because the rect is zero. But I added an UILabel as a subview of the control and overridden the setText method so, as we enter a text as usual, the text field text is nil and is the label which shows the text
- (void)setText:(NSString *)aText {
[super setText:nil];
if (aText == nil) {
textLabel_.text = nil;
}
if (![aText isEqualToString:#""]) {
textLabel_.text = aText;
}
}
With this the thing works as expected. Have you know any way to improve it?
To both disable cursor and menu I use subclass with these 2 methods:
- (CGRect)caretRectForPosition:(UITextPosition *)position {
return CGRectZero;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
[UIMenuController sharedMenuController].menuVisible = NO;
self.selectedTextRange = nil;
return NO;
}
I simply subclass UITextField, and override layoutSubviews as follows:
- (void)layoutSubviews
{
[super layoutSubviews];
for (UIView *v in self.subviews)
{
if ([[[v class] description] rangeOfString:#"UITextSelectionView"].location != NSNotFound)
{
v.hidden = YES;
}
}
}
It's a dirty hack, and may fail in the future (at which point the cursor will be visible again - your app won't crash), but it works.
If you like cleaner = less code, use the interface builder:
(Attributes inspector, view section.)
In my case, overriding the caret rect wasn't enough. On iOS 15, the caret didn't appear, effectively, but the selection handles did.
Solved it with: override var canBecomeFirstResponder: Bool { return false } on the UITextView subclass.
You can add a BOOL cursorless property to UITextField in a category via associated objects.
#interface UITextField (Cursorless)
#property (nonatomic, assign) BOOL cursorless;
#end
Then use method swizzling to swizzle caretRectForPosition: with a method that toggles between CGRectZero and its default value using cursorless.
This leads to a simple interface via a drop-in category. This is demonstrated in the following files.
Simply drop them in and get the benefit of this simple interface
UITextField category:
https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.h
https://github.com/rexmas/RexDK/blob/master/RexDK/UI/UITextField%2BRXCursorless.m
Method Swizzling:
https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.h
https://github.com/rexmas/RexDK/blob/master/RexDK/Foundation/NSObject%2BRXRuntimeAdditions.m