146

I have UITextView *_masterText and after call method setText property font is being reset. It's happening after I change sdk 7. _masterText is IBOutlet, global and properties are set in storyboard. It's only me or this is general SDK bug?

@interface myViewController : UIViewController
{
  IBOutlet UITextView *_masterText;
}

@implementation myViewController

-(void)viewWillAppear:(BOOL)animated
{
    [_masterText setText:@"New text"];
}
Juan Boero
  • 6,281
  • 1
  • 44
  • 62
Błażej
  • 3,617
  • 7
  • 35
  • 62

13 Answers13

451

Sitting with this for hours, I found the bug. If the property "Selectable" = NO it will reset the font and fontcolor when setText is used.

So turn Selectable ON and the bug is gone.

Bosse Nilsson
  • 4,546
  • 1
  • 14
  • 6
  • 15
    This happens to me also in Xcode 5. My workaround is to temporarily set selectable=YES before calling setText: – Rollin_s Oct 07 '13 at 19:01
  • Apparently, Xcode 6 final release didn't solve this problem. I'm getting the same thing on the GM release of Xcode 6. – Pan Ziyue Sep 20 '14 at 11:27
  • Is there any way to fix this issue while having not selectable? – John Mar 24 '15 at 00:36
  • 2
    @John Yes you just set selectable=TRUE, set the text, and then set selectable=FALSE again. This is still a bug in 6.3.1 – ChrisH May 07 '15 at 23:12
  • Just setting "selectable" to YES before the setText, and NO after didn't fix it for me in iOS 8.3. I found I needed to set "selectable" to YES in the storyboard, too. – Peter Johnson Jun 15 '15 at 13:14
  • I think the bug is related to the iOS version, not the Xcode version. And seems to be present in all 8.x versions I tested so far – Christophe Fondacci Sep 03 '15 at 16:56
  • Xcode 7.1.1 the same, and also the selectable property needs to be set to true in IB also – Radu Diță Dec 07 '15 at 15:18
  • 4
    This seems to be fixed in iOS 10, if your still targeting iOS 9 or below you will need to do the workaround. – user1184205 Nov 22 '16 at 20:38
11

I ran into the same issue (on Xcode 6.1) and while John Cogan's answer worked for me, I found that extending the UITextView class with a category was a better solution for my particular project.

interface

@interface UITextView (XcodeSetTextFormattingBugWorkaround)
    - (void)setSafeText:(NSString *)textValue;
@end

implementation

@implementation UITextView (XcodeSetTextFormattingBugWorkaround)
- (void)setSafeText:(NSString *)textValue
{
    BOOL selectable = [self isSelectable];
    [self setSelectable:YES];
    [self setText:textValue];
    [self setSelectable:selectable];
}
@end
Community
  • 1
  • 1
Ken Steele
  • 111
  • 1
  • 4
  • 1
    And in swift: `extension UITextView { func setSafeText(text: String){ let originalSelectable = selectable selectable = true self.text = text selectable = originalSelectable } }` – little Feb 02 '16 at 13:53
8

If you want your text view to be "read only" you can check Editable and Selectable and uncheck User Interaction Enabled, with this the UITextView was behaving as I wanted

enter image description here

enter image description here

Chuy47
  • 2,391
  • 1
  • 30
  • 29
  • 3
    This seems on the surface to be a viable solution, but ultimately is not a good option for a UITextView. Normally you choose a UITextView because you need a scrollable text area. disabling User Interaction also disables the scroll view. For me the choice of using a UITextView was because I had an area that needed to hold more text than could fit in that area, and be able to scroll. – jhelzer Nov 18 '15 at 13:40
6

Had this issue myself and the above answer helped but I added a wrapper to my ViewController code as follows and just pass the uiview instance and text to change and the wrapper function toggles the Selectable value on, changes text and then turns it off again. Helpful when you need the uitextview to be off at all times by default.

/*
    We set the text views Selectable value to YES temporarily, change text and turn it off again.
    This is a known bug that if the selectable value = NO the view loses its formatting.
 */
-(void)changeTextOfUiTextViewAndKeepFormatting:(UITextView*)viewToUpdate withText:(NSString*)textValue
{
    if(![viewToUpdate isSelectable]){
        [viewToUpdate setSelectable:YES];
        [viewToUpdate setText:textValue];
        [viewToUpdate setSelectable:NO];
    }else{
        [viewToUpdate setText:textValue];
        [viewToUpdate setSelectable:NO];
    }
}
John Cogan
  • 1,034
  • 4
  • 16
  • 39
2

EDIT :

Setting font for UITextView in iOS 7 work for me if firstly you set the text and after that you set the font :

@property (nonatomic, weak) IBOutlet UITextView *masterText;

@implementation myViewController

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    _myTextView.text = @"My Text";

    _myTextView.font = [UIFont fontWithName:@"Helvetica.ttf" size:16]; // Set Font

}

On a XIB file, if you add some text in your UITextView and change the font or the color it will work.

neowinston
  • 7,584
  • 10
  • 52
  • 83
Jordan Montel
  • 8,227
  • 2
  • 35
  • 40
2

Here's a quick subclass solution I often use for this problem.

class WorkaroundTextView: UITextView {
    override var text: String! {
        get {
            return super.text
        }
        set {
            let originalSelectableValue = self.selectable
            self.selectable = true
            super.text = newValue
            self.selectable = originalSelectableValue
        }
    }
}
WaltersGE1
  • 813
  • 7
  • 26
2

This issue resurfaced in Xcode 8. This is how I fixed it:

Changed the extension to:

extension UITextView{
    func setTextAvoidXcodeIssue(newText : String, selectable: Bool){
        isSelectable = true
        text = newText
        isSelectable = selectable
    }
}

and checked the Selectable option in the Interface Builder.

It's not very elegant to have that 'selectable' parameter but it'll do.

1

In iOS 8.3, the workaround of setting "selectable" to YES before the setText, and NO after, didn't fix it for me.

I found I needed to set "selectable" to YES in the storyboard, too, before this would work.

Peter Johnson
  • 3,764
  • 1
  • 23
  • 27
1

This worked for me:

let font = textView.font
textView.attributedText = attributedString
textView.font  = font
David Green
  • 874
  • 7
  • 5
0

I am having this problem to. A swifty-friendly solution of @Ken Steele's answer answer. I extend the UITextView and add a computed property.

extension UITextView {
    // For older Swift version output should be NSString!
    public var safeText:String!
        {
        set {
            let selectable = self.selectable;
            self.selectable = true;
            self.text = newValue;
            self.selectable = selectable;
        }
        get {
            return self.text;
        }
    }
}

hope it helps.

JelenaM
  • 3
  • 3
LastMove
  • 2,482
  • 1
  • 15
  • 25
0

For me with attributed text, I just needed to set the font in the attributes dictionary rather than setting it in it's own field.

loudmouth
  • 1,946
  • 1
  • 12
  • 16
0

Its been 3 years and the bug still exists in the latest stable version of Xcode (7.3). Clearly apple wont be fixing it any time soon leaving developers with two options: leaving selectable on and setting UserInteractionEnabled to false or Method swizzling.

If you have a button on your textView the former will not suffice.

No-code-change-requied solution in swift:

import UIKit

extension UITextView {
    @nonobjc var text: String! {
        get {
            return performSelector(Selector("text")).takeUnretainedValue() as? String ?? ""
        } set {
            let originalSelectableValue = selectable
            selectable = true
            performSelector(Selector("setText:"), withObject: newValue)
            selectable = originalSelectableValue
        }
    }
}

Objective-C:

#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@implementation UITextView (SetTextFix)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        SEL originalSelector = @selector(setText:);
        SEL swizzledSelector = @selector(xxx_setText:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod =
        class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
       }
   });
}

- (void)xxx_setText:(NSString *)text {
    BOOL originalSelectableValue = self.selectable;
    self.selectable = YES;
    [self xxx_setText:text];
    self.selectable = originalSelectableValue;
}

@end
Mark Bourke
  • 9,806
  • 7
  • 26
  • 30
0

Using the work-around discussed in this issue, this extension to UITextView provides a setTextInCurrentStyle() function. Based on solution by Alessandro Ranaldi but does not require the current isSelectable value to be passed to the function.

extension UITextView{
    func setTextInCurrentStyle(_ newText: String) {
        let selectablePreviously = self.isSelectable
        isSelectable = true
        text = newText
        isSelectable = selectablePreviously
    }
}
Duncan Babbage
  • 19,972
  • 4
  • 56
  • 93