1

i have got an issue, i can't change frame.size.width in 3rd party lib. Could not find any normal solution to change the width, so i decided to execute objc/runtime.

I've got ViewController and its property DTAttributedTextView *v.

@interface DTAttributedTextView : UIScrollView
{
 // ivars needed by subclasses
 DTAttributedTextContentView *_attributedTextContentView;
}

 @property (nonatomic, strong) NSAttributedString *attributedString;
 @property (nonatomic, DT_WEAK_PROPERTY) IBOutlet 
 id<DTAttributedTextContentViewDelegate> textDelegate;
 @property (nonatomic, strong) IBOutlet UIView *backgroundView;
 ....
@end

v got @property ( .., readonly) DTAttributedTextContentView *attributedTextContentView

 @interface DTAttributedTextContentView : UIView
 {
      NSAttributedString *_attributedString;
      DTCoreTextLayoutFrame *_layoutFrame;

      UIEdgeInsets _edgeInsets;

      NSMutableDictionary *customViewsForAttachmentsIndex;

      BOOL _flexibleHeight;

      // for layoutFrame
      NSInteger _numberOfLines;
      NSLineBreakMode _lineBreakMode;
      NSAttributedString *_truncationString;
}

attributedTextContentView got @property DTCoreTextLayoutFrame *layoutFrame

 @interface DTCoreTextLayoutFrame : NSObject 
 {
   CGRect _frame;

   NSArray *_lines;
   NSArray *_paragraphRanges;

   NSArray *_textAttachments;
   NSAttributedString *_attributedStringFragment;
 }

So basically i need to change

self.v.attributedTextContentView.layoutFrame.frame.size.width

for a pitty i cant use

objc_setAssociatedObject(self.v.attributedTextContentView.layoutFrame,@"frame.size.width",@200,OBJC_ASSOCIATION_ASSIGN);

nor

objc_setAssociatedObject(self.v.attributedTextContentView.layoutFrame,@"frame",CGRectMake(0,0,200,1000),OBJC_ASSOCIATION_ASSIGN);

because i can't access ivars by dot notation, nor send CGStruct as CGFloat required event if &.

As another solution to this situation i see creating object by object using runtime and then change the pointer. Maybe some steps could be done using copy. My problem is that im total newbie in objc/runtime and also documentation is really poor. Im struggling to learn this significant technology so i'm intentionally do not solve the exact problem using other options.

Any help would be highly appreciated. Thanx in advance.

KysokZla
  • 25
  • 5
  • BTW i solved the original issue with frame, using standart object creation. But using runtime for dynamic value changing is still actual. – KysokZla Aug 10 '16 at 12:41

1 Answers1

0

It seems that you are doing somethings wrong, but if you really need to set value to another instance ivar (this could lead to unpredictable behavior), this is what you need to know:

1) associated object is not a part of instance, so if you add associated object with key that equal property/ivar name it will not change the value of property/ivar

2) you cannot change the part of structure if it this structure is a property of object

3) you can get access to ivars by calling valueForKey: / setValue:forKey: if accessInstanceVariablesDirectly property is not overriden.

4) you need to wrap your structure to NSValue before passing to setValue:forKey:

So, the result code should look like this:

DTCoreTextLayoutFrame *layoutFrame = self.v.attributedTextContentView.layoutFrame;
CGRect frame = [[layoutFrame valueForKey:@"_frame"] CGRectValue];
frame.size.width = 200.0;
NSValue* frameValue = [NSValue valueWithCGRect:frame];
[layoutFrame setValue:frameValue forKey:@"_frame"];

For more information about KVC, check Key-Value Coding Programming Guide.

Update:

If you want to use runtime functions, you need to:

1) get offset of instance variable (all objective-c objects are structures)

2) create pointer to the ivar that you are interested in

3) read from / write to this pointer directly

This is the code:

DTCoreTextLayoutFrame *layoutFrame = self.v.attributedTextContentView.layoutFrame;    
Ivar ivar = class_getInstanceVariable([DTCoreTextLayoutFrame class], "_frame");
ptrdiff_t offset = ivar_getOffset(ivar);
CGRect *framePtr = (__bridge void*)layoutFrame + offset;
CGRect frame = *framePtr;
frame.size.width = 100;
*framePtr = frame;
Community
  • 1
  • 1
Roman Ermolov
  • 7,898
  • 5
  • 27
  • 35
  • Yeap, thanx i solved it using same approach- instiating all objects one by one, but my question is still actual how to solve this problem using runtime functions. – KysokZla Aug 11 '16 at 13:20
  • Actually, I don't create objects (except NSValue - CGRect wrapper) in solution. But I added approach, that uses runtime functions. – Roman Ermolov Aug 11 '16 at 13:55