Forum Archive

Changing ui.TextView line spacing

Enez Houad

A new problem for me with may be solved with objc and UIKit ;-)
I want to change the line spacing of a ui.TextView. I’ve followed the advice given by cvp (searching on the web) and found objc examples. Here is one that looks ok :

let tv = UITextView()
    tv.tintColor = UIColor.darkGray
    tv.font = UIFont.systemFont(ofSize: 17)
    tv.backgroundColor = UIColor.white
    let spacing = NSMutableParagraphStyle()
    spacing.lineSpacing = 4
    let attr = [NSParagraphStyleAttributeName : spacing]
    tv.typingAttributes = attr

I’ve tried to « translate » it in Pythonista, but my script never works. After 3 days of research, I'm desperate.
Please , help me ;-)

cvp

@Enez-Houad In objc, but does not do what you want

import ui
from objc_util import *
tv = ui.TextView()
tv.frame =(0,0,500,500)
tvo = ObjCInstance(tv)

spacing = ObjCClass('NSMutableParagraphStyle').alloc()
spacing.setLineSpacing_(20.0)

print('before',tvo.typingAttributes())
tvo.setTypingAttributes_({'NSParagraphStyleAttributeName':spacing})
print('after',tvo.typingAttributes())

tv.present('sheet')
Enez Houad

@cvp Thanks ! Effectively, doesn't do what I want ;-) But new tracks to explore :-))

cvp

@Enez-Houad other test following here but still without success

import ui
from objc_util import *
tv = ui.TextView()
tv.frame =(0,0,500,500)
tvo = ObjCInstance(tv)

NSMutableParagraphStyle = ObjCClass('NSMutableParagraphStyle').alloc()
NSMutableParagraphStyle.setLineSpacing_(20.0)

NSMutableAttributedString = ObjCClass('NSMutableAttributedString').alloc().initWithString_attributes_('test',{'NSParagraphStyleAttributeName':NSMutableParagraphStyle})

tvo.setAttributedText_(NSMutableAttributedString)
#tvo.setTypingAttributes_({'NSParagraphStyleAttributeName':NSMutableParagraphStyle})

tv.present('sheet')
cvp

@Enez-Houad Eureka (main problem was the Attribute key name: NSParagraphStyle without AttributeName (found here after hours...)

Last part of the script, only for the fun, tap on the red rectangle

import ui
from objc_util import *
tv = ui.TextView()
tv.frame =(0,0,500,500)
tvo = ObjCInstance(tv)

NSMutableParagraphStyle = ObjCClass('NSMutableParagraphStyle').alloc()
NSMutableParagraphStyle.setLineSpacing_(20.0)

NSMutableAttributedString = ObjCClass('NSMutableAttributedString').alloc().initWithString_('test\nabcde')
NSMutableAttributedString.setAttributes_range_({'NSParagraphStyle':NSMutableParagraphStyle},NSRange(0,10))

tvo.attributedText = NSMutableAttributedString

tv.present('sheet')

# just to show line height
txt = 'test'
i = tv.text.find(txt)
p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
p2 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i+len(txt))
rge = tvo.textRangeFromPosition_toPosition_(p1,p2)
rect = tvo.firstRectForRange_(rge)  # CGRect
x,y = rect.origin.x,rect.origin.y
w,h = rect.size.width,rect.size.height
#print(x,y,w,h)
l = ui.Button()
l.frame = (x,y,w,h)
l.background_color = (1,0,0,0.5)
l.border_width = 1
def button_action(sender):
    if l.background_color == (1,0,0,0.5):
        sender.background_color = (0,0,1,0.5)
    else:
        sender.background_color = (1,0,0,0.5)
l.action = button_action
tv.add_subview(l)
Enez Houad

@cvp Bravo ! I've seen sooner this topic but didn't understood ;-)
Thank you very much.
I'm a retired school teacher and I've developed during the last 35 last years free apps for school on Mac (Hypercard and Livecode). I'm now retired but I would like to develop this apps for iPad and Pythonista is a wonderful tool for this work. So I need to prepare my objects before making my new versions of apps. Attributed TextView is one of the object I need.
And now : Au travail !

Enez Houad

Here is my demo script (with the help of cvp) of attributed TextView. You can change text color, font and paragraph attributes.

import ui
from objc_util import *

def fake_text():
    import random, faker    
    return faker.Faker().text(random.randint(500, 500))

tv = ui.TextView(frame = (0, 0, 500, 500))
tv.editable = False
tvobj = ObjCInstance(tv)

text = fake_text()

NSMutableParagraphStyle = ObjCClass('NSMutableParagraphStyle').alloc()
NSMutableParagraphStyle.setLineSpacing_(20.0)
NSMutableParagraphStyle.setParagraphSpacing_(40.0)
NSMutableParagraphStyle.setFirstLineHeadIndent_(30.0)
NSMutableParagraphStyle.setHeadIndent_(10)
NSMutableParagraphStyle.setAlignment_(3) # justified
NSParagraphStyle = ns('NSParagraphStyle')

NSMutableAttributedString = ObjCClass('NSMutableAttributedString').alloc()
NSMutableAttributedString.initWithString_(text)

UIColor = ObjCClass('UIColor')
NSForegroundColorAttributeName = ns('NSColor')
color = UIColor.colorWithRed_green_blue_alpha_(1, 0, 0, 1)

UIFont = ObjCClass('UIFont')
NSFontAttributeName = ns('NSFont')
font = UIFont.fontWithName_size_('Optima', 18)

attributes = {  NSParagraphStyle:NSMutableParagraphStyle,
                NSForegroundColorAttributeName:color,
                NSFontAttributeName:font}
NSMutableAttributedString.setAttributes_range_(attributes, NSRange(0, len(text)))

tvobj.attributedText = NSMutableAttributedString

tv.present('sheet')
Enez Houad

I created an AttributedTextView class from my demo script. Unfortunately Pythonista crashes when I run the script several times in a row. In the faultlog, I see NSRangeException: *** - [__ NSArrayM objectAtIndex:] index 5 beyound bounds [0 .. 4] but I don't understand what it meens.
A track, please, to solve my problem…

cvp

@Enez-Houad It means that you create an array of 5 elements, like a=[1,2,3,4,5] and you try to access a[5]. You should post the code of the line giving the error.

Enez Houad

@cvp Thanks ! I'm going to search by myself (to progress ;-) and if I don't find, I'll come back for help…

Enez Houad

@cvp Eureka ! I finally solved my problem that had nothing to do with my class AttributedTextView and with objC.
Just a problem with settings assigned to an array ...

cvp

@Enez-Houad 👍