iPhone UITextView设置行间距

15

我如何增加UITextView的行间距,使其看起来像iPhone中的“Notes”应用程序?


1
对于Swift 4和iOS 11,请参见此答案,其中展示了最多3种解决问题的不同方法。 - Imanou Petit
8个回答

34

使用 Swift 4 和 iOS 11,根据您的需求,您可以选择以下3种实现方式之一来解决问题。


#1. 使用StringUIFontDescriptorSymbolicTraitstraitLooseLeading属性

traitLooseLeading的声明如下:

字体使用较松的行距值。

static var traitLooseLeading: UIFontDescriptorSymbolicTraits { get }
以下代码展示了如何实现traitLooseLeading,以便为您的UItextView设置更宽松的字体领先。
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = UITextView()
        view.addSubview(textView)

        textView.text = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        if let fontDescriptor = UIFontDescriptor
            .preferredFontDescriptor(withTextStyle: UIFontTextStyle.body)
            .withSymbolicTraits(UIFontDescriptorSymbolicTraits.traitLooseLeading) {
            let looseLeadingFont = UIFont(descriptor: fontDescriptor, size: 0)
            textView.font = looseLeadingFont
        }

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

}

#2. 使用 NSAttributedStringNSMutableParagraphStylelineSpacing 属性

lineSpacing 有以下声明:

一行片段底部和下一行顶部之间的点距离。

var lineSpacing: CGFloat { get set }
以下代码展示了如何实现lineSpacing以便在您的UItextView中为某些属性文本设置特定的行间距。
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let string = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = 15

        let attributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.paragraphStyle: paragraphStyle]
        let attributedString = NSAttributedString(string: string, attributes: attributes)

        let textView = UITextView()
        textView.attributedText = attributedString
        view.addSubview(textView)

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

}

#3. 使用 StringNSLayoutManagerDelegate 协议的 layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:) 方法

layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:) 的声明如下:

返回给定字形索引结尾的行之后的间距。[...] 在布局每一行时发送此消息,以便布局管理器代理可以自定义行的形状。

optional func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat
下面的代码展示了如何实现layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:)以便为您的UItextView设置特定的行间距。
import UIKit

class ViewController: UIViewController, NSLayoutManagerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = UITextView()
        textView.layoutManager.delegate = self
        view.addSubview(textView)

        textView.text = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

    // MARK: - NSLayoutManagerDelegate

    func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
        return 15
    }

}

作为之前代码的替代方案,下面的代码展示了如何在一个UITextView子类中实现layoutManager(_:lineSpacingAfterGlyphAt:withProposedLineFragmentRect:)方法。

import UIKit

class LineSpacingTextView: UITextView, NSLayoutManagerDelegate {

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        layoutManager.delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - NSLayoutManagerDelegate

    func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat {
        return 15
    }

}
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = LineSpacingTextView()
        view.addSubview(textView)

        textView.text = """
        Lorem ipsum
        Dolor sit amet,
        consectetur adipiscing elit
        """

        // Layout textView
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor).isActive = true
        textView.bottomAnchor.constraint(equalTo: view.readableContentGuide.bottomAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor).isActive = true
        textView.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor).isActive = true
    }

}

谢谢!最后一种实现方式是最好的。 - Hassan Taleb

24

现在在iOS6上,可以使用NSParagraphStyle,但是文档非常不完善,似乎很少起作用。

我目前正在尝试以下方式解决它:

UITextView *lab = [LocalTexts objectAtIndex:j];

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineHeightMultiple = 50.0f;
paragraphStyle.maximumLineHeight = 50.0f;
paragraphStyle.minimumLineHeight = 50.0f;

NSString *string = lab.text;
NSDictionary *ats = @{
    NSFontAttributeName: [UIFont fontWithName:@"DIN Medium" size:16.0f],
    NSParagraphStyleAttributeName: paragraphStyle, 
};

lab.attributedText = [[NSAttributedString alloc] initWithString:string attributes:ats];

问题是,当你设置字体时,行高停止起作用。非常奇怪。我还没有找到解决方法。

此外,您可以创建自定义的属性化CoreText视图...但这要技术水平更高一些,你可以在这里找到如何完成的演示。

希望有所帮助。


特别是如果文档不完善,您可能希望链接到您找到的任何好文档。 - Hawken

11

更改行间距:

NSString *textViewText =self.myTextView.text;

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:textViewText];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 30;
NSDictionary *dict = @{NSParagraphStyleAttributeName : paragraphStyle };
[attributedString addAttributes:dict range:NSMakeRange(0, [textViewText length])];

self.myTextView.attributedText = attributedString;

已确认可行。需要注意的是,如果似乎降到1没有任何变化,您可能需要采取负数来获得确切的所需结果。 - Will

10

查看UITextView的文档就足以确定该控件不支持更改行间距。

iOS 6及以上版本:

使用NSParagraphStyle可能会有一些可能性,

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineHeightMultiple = 50.0f;
paragraphStyle.maximumLineHeight = 50.0f;
paragraphStyle.minimumLineHeight = 50.0f;
NSString *string = @"your paragraph here";
NSDictionary *attribute = @{
   NSParagraphStyleAttributeName : paragraphStyle, 
   };
[textview setFont:[uifont fontwithname:@"Arial" size:20.0f]];
textview.attributedText = [[NSAttributedString alloc] initWithString:string attributes:attribute];

那么,笔记应用程序是如何允许行间距的呢? - Satyam
它将使用CoreText实现。 - KingofBliss
很高兴听到这个消息。有没有使用核心文本进行操作的教程或示例? - Satyam
这只适用于MacOS X...你需要为iPhone做很多更改。 - KingofBliss

5

针对 Swift 2.2

let paragraphStyle: NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 20.0
paragraphStyle.maximumLineHeight = 20.0
paragraphStyle.minimumLineHeight = 20.0
let ats = [NSFontAttributeName: UIFont(name: "Helvetica Neue", size: 11.0)!, NSParagraphStyleAttributeName: paragraphStyle]
cell.textView.attributedText = NSAttributedString(string: "you string here", attributes: ats)

1
let paragraphStyle = NSMutableParagraphStyle(); paragraphStyle.lineSpacing = 6 - Jindřich Skeldal

4

如果最终目的是增加行间距,你可以直接在界面构建器中进行。将Text属性设置为“Attributed”,然后单击右侧的“...”。在那里设置Spacing属性应该能正确更新您的行间距。


3
在上面的几个答案中,属性 lineHeightMultiple 被错误使用了:
paragraphStyle.lineHeightMultiple = 50.0f;

根据官方文档,lineHeightMultiple 是一种乘数,而不是字符串的绝对高度:
引用如下: “正文段落的自然行高乘以该因子(如果为正),然后再受到最小及最大行高的限制。此属性的默认值为0.0。” https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/ApplicationKit/Classes/NSParagraphStyle_Class/index.html#//apple_ref/occ/instp/NSParagraphStyle/maximumLineHeight 因此,以下代码:
paragraphStyle.lineHeightMultiple = 50.0f;
paragraphStyle.maximumLineHeight = 50.0f;
paragraphStyle.minimumLineHeight = 50.0f;

等同于

paragraphStyle.lineHeight = 50.0f

0
 NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
   paragraphStyle.lineHeightMultiple = 50.0f;
   paragraphStyle.maximumLineHeight = 50.0f;
   paragraphStyle.minimumLineHeight = 50.0f;
     NSString *string = @"if you want reduce or increase space between lines in uitextview ,you can do this with this,but donot set font on this paragraph , set this on uitextveiw.";

    NSDictionary *ats = @{
   NSParagraphStyleAttributeName : paragraphStyle, 
     };

    [textview setFont:[uifont fontwithname:@"Arial" size:20.0f]];
    textview.attributedText = [[NSAttributedString alloc] initWithString:string attributes:ats];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接