UISearchBar wont remove UISearchBarBackground - ios

I have tried EVERYTHING to get this to work. I setup a custom class like so.
override func layoutSubviews() {
super.layoutSubviews()
clearBackgroundColor() // function in the question
}
private func clearBackgroundColor() {
guard let UISearchBarBackground: AnyClass = NSClassFromString("UISearchBarBackground") else { return }
for view in self.subviews {
for subview in view.subviews {
if subview.isKind(of: UISearchBarBackground) {
subview.alpha = 0
}
}
}
}
I set backgroundColor, barTintColor to .clear. Style to minimal. Im losing my mind. I set breakpoints to make sure we are finding the search bar background. Ive tried subview.removeFromSuperview() as well. Nothing. I think Im going insane. Am I missing something?
This is on iOS 10 and am using storyboard. Any help would be greatly appreciated.

I had to do this in a client's app a while ago. Here's what worked for me:
I had a UISearchBar subclass:
#property (nonatomic, strong) UITextField* textField;
I called the following from init:
self.textField = [self findViewOfClass:[UITextField class] inView:self];
self.translucent = NO;
self.barTintColor = ...;
self.textField.backgroundColor = ...;
- (id)findViewOfClass:(Class)class inView:(UIView*)view
{
if ([view isKindOfClass:class])
{
return view;
}
else
{
for (UIView* subview in view.subviews)
{
id foundView = [self findViewOfClass:class inView:subview];
if (foundView != nil)
{
return foundView;
}
}
}
return nil;
}
The essential part is finding the UITextField. (I did a similar thing to allow me to custom style the cancel button.) I vaguely remember that disabling translucent was really needed; easy to try.
That should be it. Let me know if this works for you.
I only have Obj-C code, but this is easy to convert.

I finally ignored previous answers from all the posts about this subject and did my own Debug View Hierarchy. I spotted a ImageView that serves as the background which I guess is now called "_UISearchBarSearchFieldBackgroundView". This helped me find a single function that fixes the problem at least for iOS 9+.
searchBar.setSearchFieldBackgroundImage(UIImage(), for: .normal)
One thing to note is that this isn't the only way to fix this problem. However, I used it because it requires no looping and because the image is empty the additional view is never added giving the same end result as other methods.
One thing to note is that this may only work for iOS 9+. So, your milage may vary. I tested with iOS 10 with a Deployment Target of 9.3.

Related

How to disable iOS 11 and iOS 12 Drag & Drop in WKWebView?

Long-pressing images or links in a WKWebView on iOS 11 and 12 initiates a Drag & Drop session (the user can drag the image or the link). How can I disable that?
I did find a solution that involves method swizzling but it's also possible to disable drag and drop in a WKWebView without any swizzling.
Note: See special notes for iOS 12.2+ below
WKContentView — a private subview of WKWebView's WKScrollView — has an interactions property, just like any other UIView in iOS 11+. That interactions property contains both a UIDragInteraction and a UIDropInteraction. Simply setting enabled to false on the UIDragInteraction does the trick.
We don't want to access any private APIs and make the code as solid as possible.
Assuming your WKWebView is called webView:
if (#available(iOS 11.0, *)) {
// Step 1: Find the WKScrollView - it's a subclass of UIScrollView
UIView *webScrollView = nil;
for (UIView *subview in webView.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
webScrollView = subview;
break;
}
}
if (webScrollView) {
// Step 2: Find the WKContentView
UIView *contentView = nil;
// We don't want to trigger any private API usage warnings, so instead of checking
// for the subview's type, we simply look for the one that has two "interactions" (drag and drop)
for (UIView *subview in webScrollView.subviews) {
if ([subview.interactions count] > 1) {
contentView = subview;
break;
}
}
if (contentView) {
// Step 3: Find and disable the drag interaction
for (id<UIInteraction> interaction in contentView.interactions) {
if ([interaction isKindOfClass:[UIDragInteraction class]]) {
((UIDragInteraction *) interaction).enabled = NO;
break;
}
}
}
}
}
That's it!
Special note for iOS 12.2+
The above code still works on iOS 12.2, but it is important when to call it. On iOS 12.1 and below you could call this code right after creating the WKWebView. That's not possible anymore. The WKContentView's interactions array is empty when it's first created. It is only populated after the WKWebView is added to a view hierarchy that is attached to a UIWindow - simply adding it to a superview that is not yet part of the visible view hierarchy is not enough. In a view controller viewDidAppear would most likely be a safe place to call it from.
How did I find this out?
I searched through the WebKit source and found this: https://github.com/WebKit/webkit/blob/65619d485251a3ffd87b48ab29b342956f3dcdc7/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm#L4953
That's the method that creates and adds the UIDragInteraction
It turns out that this method (setupDataInteractionDelegates) actually exists on WKContentView
So I set a symbolic breakpoint on -[WKContentView setupDataInteractionDelegates]
The breakpoint was hit
I used lldb to print the backtrace using the bt command
This was the output:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 50.1
* frame #0: 0x00000001115b726c WebKit`-[WKContentView(WKInteraction) setupDataInteractionDelegates]
frame #1: 0x00000001115a8852 WebKit`-[WKContentView(WKInteraction) setupInteraction] + 1026
frame #2: 0x00000001115a5155 WebKit`-[WKContentView didMoveToWindow] + 79
So clearly the creation and addition of the UIDragInteraction is triggered by the view moving to (being added to) a window.
This works great!
Thanks #basha for the swift version.
I did the same but with some compactMaps to reduce the depth of the if-statements and guards to get rid of the force unwraps.
private func disableDragAndDropInteraction() {
var webScrollView: UIView? = nil
var contentView: UIView? = nil
if #available(iOS 11.0, *) {
guard let noDragWebView = webView else { return }
webScrollView = noDragWebView.subviews.compactMap { $0 as? UIScrollView }.first
contentView = webScrollView?.subviews.first(where: { $0.interactions.count > 1 })
guard let dragInteraction = (contentView?.interactions.compactMap { $0 as? UIDragInteraction }.first) else { return }
contentView?.removeInteraction(dragInteraction)
}
}
Based on Johannes FahrenKrug's Post, with some changes.
private func disableDragAndDropInteraction() {
var webScrollView: UIView? = nil
var contentView: UIView? = nil
if #available(iOS 11.0, *) {
if (webView != nil) {
for subView in webView!.subviews {
if (subView is UIScrollView) {
webScrollView = subView
break
}
}
if (webScrollView != nil) {
for subView in webScrollView!.subviews {
if subView.interactions.count > 1 {
contentView = subView
break
}
}
if (contentView != nil) {
for interaction in contentView!.interactions {
if interaction is UIDragInteraction {
contentView!.removeInteraction(interaction)
}
}
}
}
} else {
// Fallback on earlier versions
}
}
}
Use the CSS property webkit-touch-callout on the img and a elements. Also set their draggable attribute to false by changing the HTML or injecting it using -[WKWebView evaluateJavaScript(...)].
img, a {
-webkit-touch-callout: none;
}
<img draggable="false">
Avoids all the fragile subview diving and manipulation.
(Recap of my answer to the similar Disable link drag on WKWebView .)
My approach was to subclass WKWebView and search the view hierarchy for subviews that contain drag or drop interactions, recursively. Once a view is found, which most likely will be a WKContentView, the interactions are removed. The benefit of this method is to not rely on any subview order / view hierarchy, which could change between OS releases.
override func didMoveToWindow() {
super.didMoveToWindow()
disableDragAndDrop()
}
func disableDragAndDrop() {
func findInteractionView(in subviews: [UIView]) -> UIView? {
for subview in subviews {
for interaction in subview.interactions {
if interaction is UIDragInteraction {
return subview
}
}
return findInteractionView(in: subview.subviews)
}
return nil
}
if let interactionView = findInteractionView(in: subviews) {
for interaction in interactionView.interactions {
if interaction is UIDragInteraction || interaction is UIDropInteraction {
interactionView.removeInteraction(interaction)
}
}
}
}

left circle in UITableViewCell editing mode appears in iOS8

So I just installed Xcode 6GM and fiddled with my iOS7 app on simulator running iOS8.
I have a UITableView that's in editing mode and there's now a circle on the left side of the cell which doesn't appear when running on iOS7.
I glanced at the documentation for iOS8, but I don't see any new constants and I'm using UITableViewCellEditingStyleNone and UITableViewCellSelectionStyleNone.
That circle disappears when tableView.editing = NO, also allowsMultipleSelectionDuringEditing = YES.
If anyone can tell me what's going on that'd be great :)
EDIT: compiling from XCode6GM onto my iPhone running iOS7.1 gives me the circle too. I suspect a bug with XCode6GM?
Here is a screenshot with the circles:
I just had this annoying issue while migrating my app to iOS8.
Here is the workaround I found ... add something like this in your UITableViewCell subclass:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
for( UIView* subview in self.subviews )
if( [NSStringFromClass(subview.class) isEqualToString:#"UITableViewCellEditControl"] )
subview.hidden = YES;
}
I hope this will be documented / fixed soon ...
I think I have a better solution, add this code to your custom uitableviewcell:
- (void)addSubview:(UIView *)view {
[super addSubview:view];
if( [NSStringFromClass(view.class) isEqualToString:#"UITableViewCellEditControl"] ) {
view.hidden = YES
}
}
Here's a Swift solution combining the two answers:
override func addSubview(view: UIView) {
super.addSubview(view)
if view.isKindOfClass(NSClassFromString("UITableViewCellEditControl")!) {
view.hidden = true
}
}
Here is the Swift3 version:
override func addSubview(_ view: UIView) {
super.addSubview(view)
if view.classAsString() == "UITableViewCellEditControl" {
view.isHidden = true
}
}

Turning off dimming by UIPopoverController

In iOS7, a popover causes the rest of the screen to be dimmed. As per the Apple docs:
The popover content is layered on top of your existing content and the background is dimmed automatically.
This is nice in most cases, but I have an app where the screen rearranges itself when the popover opens and stays responsive, so the dimming only causes confusion. Anyone knows if dimming can be disabled?
Doesn’t look like there’s anything in the API to support that—you can set the passthroughViews property to allow other views to be interacted with while the popover’s open, but that doesn’t affect the dimming. You may have to roll your own popover implementation or find a third-party version.
I can suggest you a custom control which is really nice work by its author. It do not dim the background. Further it has many customization.
Here is the github link for WYPopoverController
For me at works like this. I just work through all subviews if key window view, find _UIMirrorNinePatchView. _UIMirrorNinePatchView is apple class for that has four image views, these image views create the dimming background for 4 directions of PopOverPresentationController. More specifically you can look at this if you use view hierarchy debugger. So I walk through the array of these UIImageView and set UIImage to nil. This code paste in viewWillAppear of your destination controller(popOverContoller).
NSArray<UIView *> *arrayOfSubviews = [UIApplication sharedApplication].keyWindow.subviews.lastObject.subviews;
for (int i = 0; i < arrayOfSubviews.count; i++) {
if ([NSStringFromClass(arrayOfSubviews[i].class) isEqualToString:#"_UIMirrorNinePatchView"]) {
arrayOfSubviews[i].backgroundColor = [UIColor clearColor];
NSArray<UIImageView *> *arrayOfImageViews = arrayOfSubviews[i].subviews;
for (int j = 0; j < arrayOfImageViews.count; j++) {
arrayOfImageViews[j].image = nil;
}
}
}
In whole my UIPopOverController looks like this
And in view debugger, it looks so
So as you can understand, setting UIImage to nil will remove this dimming view.
This is the swift version to remove the dimming of UIPopoverController
let allSubViews: [UIView] = (UIApplication.shared.keyWindow?.subviews.last?.subviews)!
for index in 0...allSubViews.count - 1 {
allSubViews[index].removeFromSuperview()
if NSStringFromClass(allSubViews[index].classForCoder) == "_UIMirrorNinePatchView"
{
allSubViews[index].backgroundColor = UIColor.clear
let arrayofImages = allSubViews[index].subviews as! [UIImageView]
for imageIndex in 0...arrayofImages.count - 1 {
arrayofImages[imageIndex].image = nil
}
}
}
You can prevent the dimming by setting the UIPopoverBackgroundView for your popover and setting the background to be transparent for the background view.
You will need to re-implement how the popover draws the arrows, but you can find plenty of examples for that online.
Updated to work in iOS 13 with Swift 4
guard let transitionSubviews = UIApplication.shared.keyWindow?.subviews.last?.subviews else { return }
func findViews<T>(inView view: UIView, subclassOf targetType: T.Type) -> [T] {
return recursiveSubviews(inView: view).compactMap { $0 as? T }
}
func recursiveSubviews(inView view: UIView) -> [UIView] {
return view.subviews + view.subviews.flatMap { recursiveSubviews(inView: $0) }
}
for view in transitionSubviews {
view.backgroundColor = UIColor.clear
for imageView in findViews(inView: view, subclassOf: UIImageView.self) {
imageView.image = nil
}
}
If you choose to implement your custom UIPopoverBackgroundView, you can set the layer background to be clear - layer.shadowColor = UIColor.clearColor().CGColor.
However this will eliminate the dim and the shadow completely so you will have to put a border around the controller
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
This solved my problem with navigation bar dimming effect while transiting.

Change order of read items with VoiceOver

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

Hide the cursor of a UITextField

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

Resources