ios 如何在 Swift 中使用下标和上标?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29225779/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:19:12  来源:igfitidea点击:

How do I use subscript and superscript in Swift?

iosswift

提问by Adam Young

I want my UILabelto display text in following manner 6.022*1023. What functions does Swift have for subscript and superscript?

我希望我UILabel以以下方式显示文本 6.022*10 23。Swift 对下标和上标有哪些功能?

回答by Forrest Porter

Most of the answers+examples are in ObjC, but this is how to do it in Swift.

大多数答案+示例都在 ObjC 中,但这是在 Swift 中的方法。

let font:UIFont? = UIFont(name: "Helvetica", size:20)
let fontSuper:UIFont? = UIFont(name: "Helvetica", size:10)
let attString:NSMutableAttributedString = NSMutableAttributedString(string: "6.022*1023", attributes: [.font:font!])
attString.setAttributes([.font:fontSuper!,.baselineOffset:10], range: NSRange(location:8,length:2))
labelVarName.attributedText = attString

This gives me:

这给了我:

SuperScript Example

上标示例

In a more detailed explanation:

在更详细的解释中:

  1. Get UIFontyou want for both the default and superscript style, superscript must be smaller.
  2. Create a NSMutableAttributedStringwith the full string and default font.
  3. Add an attribute to the characters you want to change (NSRange), with the smaller/subscript UIFont, and the NSBaselineOffsetAttributeNamevalue is the amount you want to offset it vertically.
  4. Assign it to your UILabel
  1. 得到UIFont你想要的默认和上标样式,上标必须更小。
  2. NSMutableAttributedString使用完整的字符串和默认字体创建一个。
  3. 为要更改的字符 ( NSRange)添加一个属性,使用更小/下标UIFont,其NSBaselineOffsetAttributeName值是您要垂直偏移的量。
  4. 分配给你的 UILabel

Hopefully this helps other Swift devs as I needed this as well.

希望这可以帮助其他 Swift 开发人员,因为我也需要它。

回答by Chris

As a different approach, I wrote a function that takes in a string where the exponents are prepended with ^such as 2^2?3?5^2and returns 22?3?52

作为一种不同的方法,我编写了一个函数,该函数接受一个字符串,其中指数前面带有^例如2^2?3?5^2并返回22?3?52

func exponentize(str: String) -> String {

    let supers = [
        "1": "\u{00B9}",
        "2": "\u{00B2}",
        "3": "\u{00B3}",
        "4": "\u{2074}",
        "5": "\u{2075}",
        "6": "\u{2076}",
        "7": "\u{2077}",
        "8": "\u{2078}",
        "9": "\u{2079}"]

    var newStr = ""
    var isExp = false
    for (_, char) in str.characters.enumerate() {
        if char == "^" {
            isExp = true
        } else {
            if isExp {
                let key = String(char)
                if supers.keys.contains(key) {
                    newStr.append(Character(supers[key]!))
                } else {
                    isExp = false
                    newStr.append(char)
                }
            } else {
                newStr.append(char)
            }
        }
    }
    return newStr
}

It's a bit of a brute force method, but it works if you don't want to deal with attributed strings or you want your string to be independent of a font.

这是一种蛮力方法,但如果您不想处理属性字符串或希望您的字符串独立于字体,它就可以工作。

回答by Atka

I wrote the following extension or you can use it as a function, it is working well for me . you can modify it by skipping the parts that are not essential to you

我编写了以下扩展,或者您可以将其用作函数,它对我来说效果很好。你可以通过跳过对你来说不重要的部分来修改它

extension NSMutableAttributedString
{
enum scripting : Int
{
    case aSub = -1
    case aSuper = 1
}

func characterSubscriptAndSuperscript(string:String,
                                      characters:[Character],
                                      type:scripting,
                                      fontSize:CGFloat,
                                      scriptFontSize:CGFloat,
                                      offSet:Int,
                                      length:[Int],
                                      alignment:NSTextAlignment)-> NSMutableAttributedString
{
    let paraghraphStyle = NSMutableParagraphStyle()
     // Set The Paragraph aligmnet , you can ignore this part and delet off the function
    paraghraphStyle.alignment = alignment

    var scriptedCharaterLocation = Int()
    //Define the fonts you want to use and sizes
    let stringFont = UIFont.boldSystemFont(ofSize: fontSize)
    let scriptFont = UIFont.boldSystemFont(ofSize: scriptFontSize)
     // Define Attributes of the text body , this part can be removed of the function
    let attString = NSMutableAttributedString(string:string, attributes: [NSFontAttributeName:stringFont,NSForegroundColorAttributeName:UIColor.black,NSParagraphStyleAttributeName: paraghraphStyle])

    // the enum is used here declaring the required offset
    let baseLineOffset = offSet * type.rawValue
    // enumerated the main text characters using a for loop
    for (i,c) in string.characters.enumerated()
    {
        // enumerated the array of first characters to subscript
        for (theLength,aCharacter) in characters.enumerated()
        {
            if c == aCharacter
            {
               // Get to location of the first character
                scriptedCharaterLocation = i
              //Now set attributes starting from the character above     
               attString.setAttributes([NSFontAttributeName:scriptFont,
              // baseline off set from . the enum i.e. +/- 1          
              NSBaselineOffsetAttributeName:baseLineOffset,
              NSForegroundColorAttributeName:UIColor.black],
               // the range from above location 
        range:NSRange(location:scriptedCharaterLocation,
         // you define the length in the length array 
         // if subscripting at different location 
         // you need to define the length for each one
         length:length[theLength]))

            }
        }
    }
    return attString}
  }

examples:

例子:

let attStr1 = NSMutableAttributedString().characterSubscriptAndSuperscript(
               string: "23 x 456", 
               characters:["3","5"], 
               type: .aSuper, 
               fontSize: 20, 
               scriptFontSize: 15, 
               offSet: 10, 
               length: [1,2], 
               alignment: .left)

enter image description here

在此处输入图片说明

let attStr2 = NSMutableAttributedString().characterSubscriptAndSuperscript(
           string: "H2SO4", 
           characters: ["2","4"], 
           type: .aSub, 
           fontSize: 20, 
           scriptFontSize: 15, 
            offSet: 8, 
           length: [1,1], 
           alignment: .left)

enter image description here

在此处输入图片说明

回答by Glenn Howes

If you can get along with text that doesn't look perfect, and only need a subset of characters you can make use of the unicode superscript and subscript numbers: ? 1 2 3 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? This has the advantage of being a lot less cumbersome.

如果您可以处理看起来不完美的文本,并且只需要一个字符子集,您可以使用 unicode 上标和下标数字: ? 1 2 3 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 这具有不那么麻烦的优点。

回答by Jeehut

For a simple to use Swift solution, you might want to checkout HandyUIKit. After importing it into your project (e.g. via Carthage – see instructions in README) you can do something like this:

对于简单易用的 Swift 解决方案,您可能需要查看HandyUIKit。将其导入您的项目后(例如通过 Carthage – 请参阅自述文件中的说明),您可以执行以下操作:

import HandyUIKit

"6.022*10^{23}".superscripted(font: UIFont.systemFont(ofSize: 20, weight: .medium))

This line will return an NSAttributedStringwhich will look exactly like what you're looking for. Just assign itto a UILabels attributedTextproperty and that's it!

这一行将返回一个NSAttributedString看起来与您正在寻找的完全一样。只需将其分配UILabelsattributedText属性即可



If you're looking for subscriptinga text, simply use subscripted(font:)instead. It will recognize structures like CO_{2}. There's also superAndSubscripted(font:)if you want to combine both.

如果您正在寻找下标文本,只需使用即可subscripted(font:)。它会识别像CO_{2}. 还有superAndSubscripted(font:),如果你想结合两者

See the docsfor more information and additional examples.

有关更多信息和其他示例,请参阅文档

回答by SafeFastExpressive

Here is a simple version that has correct error handling and will compile in playground.

这是一个简单的版本,它具有正确的错误处理,并将在操场上编译。

import UIKit

func setMyLabelText(myLabel: UILabel) {
    if let largeFont = UIFont(name: "Helvetica", size: 20), let superScriptFont = UIFont(name: "Helvetica", size:10) {
        let numberString = NSMutableAttributedString(string: "6.022*10", attributes: [.font: largeFont])
        numberString.append(NSAttributedString(string: "23", attributes: [.font: superScriptFont, .baselineOffset: 10]))
        myLabel.attributedText = numberString
    }
}

let myLabel = UILabel()
setMyLabelText(myLabel: myLabel)

回答by Abhishek Thapliyal

Swift 4+ Version of @Atka's Answer

@Atka 答案的 Swift 4+ 版本

import UIKit

extension NSMutableAttributedString {

    enum Scripting : Int {
        case aSub = -1
        case aSuper = 1
    }

    func scripts(string: String,
                  characters: [Character],
                  type: Scripting,
                  stringFont: UIFont,
                  fontSize: CGFloat,
                  scriptFont: UIFont,
                  scriptFontSize: CGFloat,
                  offSet: Int,
                  length: [Int],
                  alignment: NSTextAlignment) -> NSMutableAttributedString {

        let paraghraphStyle = NSMutableParagraphStyle()
        paraghraphStyle.alignment = alignment

        var scriptedCharaterLocation = Int()

        let attributes = [
              NSAttributedStringKey.font: stringFont,
              NSAttributedStringKey.foregroundColor: UIColor.black,
              NSAttributedStringKey.paragraphStyle: paraghraphStyle
        ]

        let attString = NSMutableAttributedString(string:string, attributes: attributes)

        let baseLineOffset = offSet * type.rawValue

        let scriptTextAttributes: [NSAttributedStringKey : Any] = [
            NSAttributedStringKey.font: scriptFont,
            NSAttributedStringKey.baselineOffset: baseLineOffset,
            NSAttributedStringKey.foregroundColor: UIColor.blue
        ]

        for (i,c) in string.enumerated() {

            for (theLength, aCharacter) in characters.enumerated() {
                if c == aCharacter {
                    scriptedCharaterLocation = i
                    attString.setAttributes(scriptTextAttributes, range: NSRange(location:scriptedCharaterLocation,
                                                                                 length: length[theLength]))
                }
            }
        }
        return attString
    }
}

回答by gorillaz

A nice simple function that outputs a number as the superscript text.

一个很好的简单函数,输出一个数字作为上标文本。

func exponent(i: Int) -> String {
    let powers : [String] = [
      "\u{2070}",
      "\u{00B9}",
      "\u{00B2}",
      "\u{00B3}",
      "\u{2074}",
      "\u{2075}",
      "\u{2076}",
      "\u{2077}",
      "\u{2078}",
      "\u{2079}"
    ]

    let digits = Array(String(i))
    var string = ""

    for d in digits {
      string.append("\(powers[Int(String(d))!])")
    }
    return string
}

回答by henrik-dmg

I have created a String extension which takes a string and converts all of its superscript into unicode characters. This way you could for example share the resulting string without any hassle.

我创建了一个字符串扩展,它接受一个字符串并将其所有上标转换为 unicode 字符。例如,您可以通过这种方式轻松共享结果字符串。

extension Character {
    var unicode: String {
        // See table here: https://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts
        let unicodeChars = [Character("0"):"\u{2070}",
                            Character("1"):"\u{00B9}",
                            Character("2"):"\u{00B2}",
                            Character("3"):"\u{00B3}",
                            Character("4"):"\u{2074}",
                            Character("5"):"\u{2075}",
                            Character("6"):"\u{2076}",
                            Character("7"):"\u{2077}",
                            Character("8"):"\u{2078}",
                            Character("9"):"\u{2079}",
                            Character("i"):"\u{2071}",
                            Character("+"):"\u{207A}",
                            Character("-"):"\u{207B}",
                            Character("="):"\u{207C}",
                            Character("("):"\u{207D}",
                            Character(")"):"\u{207E}",
                            Character("n"):"\u{207F}"]

        if let unicode = unicodeChars[self] {
            return unicode
        }

        return String(self)
    }
}

extension String {
    var unicodeSuperscript: String {
        let char = Character(self)
        return char.unicode
    }

    func superscripted() -> String {
        let regex = try! NSRegularExpression(pattern: "\^\{([^\}]*)\}")
        var unprocessedString = self
        var resultString = String()

        while let match = regex.firstMatch(in: unprocessedString, options: .reportCompletion, range: NSRange(location: 0, length: unprocessedString.count)) {
                // add substring before match
                let substringRange = unprocessedString.index(unprocessedString.startIndex, offsetBy: match.range.location)
                let subString = unprocessedString.prefix(upTo: substringRange)
                resultString.append(String(subString))

                // add match with subscripted style
                let capturedSubstring = NSAttributedString(string: unprocessedString).attributedSubstring(from: match.range(at: 1)).mutableCopy() as! NSMutableAttributedString
                capturedSubstring.string.forEach { (char) in
                    let superScript = char.unicode
                    let string = NSAttributedString(string: superScript)
                    resultString.append(string.string)
                }

                // strip off the processed part
                unprocessedString.deleteCharactersInRange(range: NSRange(location: 0, length: match.range.location + match.range.length))
        }

        // add substring after last match
        resultString.append(unprocessedString)
        return resultString
    }

    mutating func deleteCharactersInRange(range: NSRange) {
        let mutableSelf = NSMutableString(string: self)
        mutableSelf.deleteCharacters(in: range)
        self = mutableSelf as String
    }
}

For example "x^{4+n}+12^{3}".superscripted()produces "x???+123"

例如"x^{4+n}+12^{3}".superscripted()产生"x???+123"

This was inspired by HandyUIKitand the gist to my code is on Github

这受到HandyUIKit 的启发,我的代码的要点在Github 上

回答by Sajjon

Here's a Swift 5.1 solution (should work with older versions of Swift too) using recursion, that only focuses outputting a superscript from an Int(i.e. no formatting for display).

这是一个使用递归的 Swift 5.1 解决方案(也应该与旧版本的 Swift 一起使用),它只关注从 an 输出上标Int(即没有显示格式)。

extension Int {
    func superscriptString() -> String {
        let minusPrefixOrEmpty: String = self < 0 ? Superscript.minus : ""
        let (quotient, remainder) = abs(self).quotientAndRemainder(dividingBy: 10)
        let quotientString = quotient > 0 ? quotient.superscriptString() : ""
        return minusPrefixOrEmpty + quotientString + Superscript.value(remainder)
    }
}

enum Superscript {
    static let minus = "?"
    private static let values: [String] = [
        "?",
        "1",
        "2",
        "3",
        "?",
        "?",
        "?",
        "?",
        "?",
        "?"
    ]

    static func value(_ int: Int) -> String {
        assert(int >= 0 && int <= 9)
        return values[int]
    }
}

Here are some tests to prove correctness:

以下是一些证明正确性的测试:

 func testPositiveIntegersSuperscript() {
        XCTAssertEqual(0.superscriptString(), "?")
        XCTAssertEqual(1.superscriptString(), "1")
        XCTAssertEqual(2.superscriptString(), "2")
        XCTAssertEqual(3.superscriptString(), "3")
        XCTAssertEqual(4.superscriptString(), "?")
        XCTAssertEqual(5.superscriptString(), "?")
        XCTAssertEqual(6.superscriptString(), "?")
        XCTAssertEqual(7.superscriptString(), "?")
        XCTAssertEqual(8.superscriptString(), "?")
        XCTAssertEqual(9.superscriptString(), "?")
        XCTAssertEqual(10.superscriptString(), "1?")
        XCTAssertEqual(11.superscriptString(), "11")
        XCTAssertEqual(12.superscriptString(), "12")

        XCTAssertEqual(19.superscriptString(), "1?")
        XCTAssertEqual(20.superscriptString(), "2?")
        XCTAssertEqual(21.superscriptString(), "21")

        XCTAssertEqual(99.superscriptString(), "??")
        XCTAssertEqual(100.superscriptString(), "1??")
        XCTAssertEqual(101.superscriptString(), "1?1")
        XCTAssertEqual(102.superscriptString(), "1?2")

        XCTAssertEqual(237.superscriptString(), "23?")

        XCTAssertEqual(999.superscriptString(), "???")
        XCTAssertEqual(1000.superscriptString(), "1???")
        XCTAssertEqual(1001.superscriptString(), "1??1")

        XCTAssertEqual(1234.superscriptString(), "123?")
        XCTAssertEqual(1337.superscriptString(), "133?")
    }


    func testNegativeIntegersSuperscript() {
        XCTAssertEqual(Int(-1).superscriptString(), "?1")
        XCTAssertEqual(Int(-2).superscriptString(), "?2")
        XCTAssertEqual(Int(-3).superscriptString(), "?3")
        XCTAssertEqual(Int(-4).superscriptString(), "??")
        XCTAssertEqual(Int(-5).superscriptString(), "??")
        XCTAssertEqual(Int(-6).superscriptString(), "??")
        XCTAssertEqual(Int(-7).superscriptString(), "??")
        XCTAssertEqual(Int(-8).superscriptString(), "??")
        XCTAssertEqual(Int(-9).superscriptString(), "??")
        XCTAssertEqual(Int(-10).superscriptString(), "?1?")
        XCTAssertEqual(Int(-11).superscriptString(), "?11")
        XCTAssertEqual(Int(-12).superscriptString(), "?12")

        XCTAssertEqual(Int(-19).superscriptString(), "?1?")
        XCTAssertEqual(Int(-20).superscriptString(), "?2?")
        XCTAssertEqual(Int(-21).superscriptString(), "?21")

        XCTAssertEqual(Int(-99).superscriptString(), "???")
        XCTAssertEqual(Int(-100).superscriptString(), "?1??")
        XCTAssertEqual(Int(-101).superscriptString(), "?1?1")
        XCTAssertEqual(Int(-102).superscriptString(), "?1?2")

        XCTAssertEqual(Int(-237).superscriptString(), "?23?")

        XCTAssertEqual(Int(-999).superscriptString(), "????")
        XCTAssertEqual(Int(-1000).superscriptString(), "?1???")
        XCTAssertEqual(Int(-1001).superscriptString(), "?1??1")

        XCTAssertEqual(Int(-1234).superscriptString(), "?123?")
        XCTAssertEqual(Int(-1337).superscriptString(), "?133?")
    }

My solution is more than twice as fast as gorillaz' solution(which is string and array based), thanks to mine being math and recursion based. Here is proof:

我的解决方案比gorillaz 的解决方案(基于字符串和数组)快两倍多,这要归功于我的基于数学和递归的解决方案。这是证明:

  private typealias SuperscriptVector = (value: Int, expectedSuperstring: String)
    private let vector1to9: SuperscriptVector = (123456789, "123??????")

    func performanceTest(times n: Int, function: (Int) -> () -> String) {
        func manyTimes(_ times: Int) {
            func doTest(vector: SuperscriptVector) {
                let result: String = function(vector.value)()
                XCTAssertEqual(result, vector.expectedSuperstring)
            }
            for _ in 0..<times {
                doTest(vector: vector1to9)
            }
        }

        manyTimes(n)
    }

    // 3.244 sec
    func testPerformanceMine() {
        measure {
            performanceTest(times: 1_000_000, function: Int.superscriptString)

        }
    }


    // 7.6 sec
    func testPerformanceStackOverflow() {
        measure {
            performanceTest(times: 1_000_000, function: Int.superscriptStringArrayBased)

        }
    }