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

时间:2015-03-24 05:43:20

标签: ios swift

我希望我的UILabel以下列方式显示文本6.022 * 10 23 。对下标和上标有什么样的影响?

15 个答案:

答案 0 :(得分:71)

大多数答案+示例都在ObjC中,但这是如何在Swift中完成的。

{"status":{"code":311,"message":"Service Request Successfully Queried.","cause":""},"Response":{"LastPage":"true","NumOutputObjects":"0","ListOfServiceRequest":{}}}

这给了我:

SuperScript Example

更详细的解释:

  1. 获取默认和上标样式的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 ,上标必须更小。
  2. 使用完整字符串和默认字体创建UIFont
  3. 使用较小的/下标NSMutableAttributedString为要更改的字符(NSRange)添加属性,UIFont值是您要垂直偏移的值。
  4. 将其分配到NSBaselineOffsetAttributeName
  5. 希望这也有助于其他Swift开发者,因为我也需要它。

答案 1 :(得分:11)

作为一种不同的方法,我编写了一个函数,该函数接收一个字符串,其中指数的前缀为^,例如2^2•3•5^2并返回2²•3•5²

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
}

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

答案 2 :(得分:10)

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

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}
  }

的示例:

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

答案 3 :(得分:9)

如果您可以使用看起来不完美的文本,只需要一个字符子集就可以使用unicode上标和下标数字:⁰¹³⁴⁵⁶⁷⁸⁹₂₂ ₄₆₇₈₉ 这具有不那么麻烦的优点。

答案 4 :(得分:4)

对于简单易用的Swift解决方案,您可能需要结帐 HandyUIKit 。将其导入项目后(例如通过Carthage - 参见README中的说明),您可以执行以下操作:

import HandyUIKit

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

此行将返回NSAttributedString,其外观与您要查找的内容完全相同。只需将其分配给UILabel attributedText 属性,然后即可!

如果您正在寻找下标文字,请改用subscripted(font:)。它将识别CO_{2}之类的结构。如果你想组合两者,还有superAndSubscripted(font:)

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

答案 5 :(得分:1)

这是一个具有正确错误处理的简单版本,将在游乐场中编译。

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)

答案 6 :(得分:1)

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

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
}

答案 7 :(得分:1)

在SwiftUI中,可以通过使用baselineOffset修饰符来实现上标效果。例如:

            Text("$")
                .foregroundColor(Color.white)
                .font(.custom(AppTheme.getRegularFont(), size: 13))
                .baselineOffset(8.0)
            
            Text("20")
                .foregroundColor(AppTheme.primaryColor)
                .font(.custom(AppTheme.getRegularFont(), size: 25))

这是它的外观:

enter image description here

答案 8 :(得分:1)

我作为String扩展的解决方案

extension String {
func setAsSuperscript(_ textToSuperscript: String) -> NSMutableAttributedString {
    let attributedString = NSMutableAttributedString(string: self)
    let foundRange = attributedString.mutableString.range(of: textToSuperscript)
    
    let font = UIFont.systemFont(ofSize: 12)

    if foundRange.location != NSNotFound {
        attributedString.addAttribute(.font, value: font, range: foundRange)
        attributedString.addAttribute(.baselineOffset, value: 3, range: foundRange)
        attributedString.addAttribute(.foregroundColor, value: UIColor.red, range: foundRange)
    }
    
    return attributedString
}

和用法:

let placeholder = "Required value*".setAsSuperscript("*")

答案 9 :(得分:0)

我创建了一个String扩展,该扩展接受一个字符串并将其所有上标转换为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
    }
}

例如"x^{4+n}+12^{3}".superscripted()产生"x⁴⁺ⁿ+12³"

这受HandyUIKit的启发,我的代码要旨位于Github

答案 10 :(得分:0)

我创建了一个AmountFormatter类,该类帮助我将十进制数字转换为带有小数点后一位的数字。

class AmountFormatter {

    static func sharedFormatter(
        decimalNumber: NSDecimalNumber,
        currency: String,
        raisedDecimals: Bool) -> NSAttributedString {
        let numberFormatter = NumberFormatter()
        numberFormatter.usesGroupingSeparator = true
        numberFormatter.groupingSeparator = "."
        numberFormatter.decimalSeparator = ","
        numberFormatter.numberStyle = .decimal

        let scale: Int16 = 2
        let behavior = NSDecimalNumberHandler(
            roundingMode: .plain,
            scale: scale,
            raiseOnExactness: false,
            raiseOnOverflow: false,
            raiseOnUnderflow: false,
            raiseOnDivideByZero: true)
        guard let amountString = numberFormatter.string(
            from: decimalNumber.rounding(accordingToBehavior: behavior))
            else {
                fatalError("Can't convert conversion from 'NSDecimalNumber' to string")
        }
        let currencyAmountString = currency + amountString

        let font = UIFont(name: "Roboto", size: 20)
        let fontSuper = UIFont(name: "Roboto", size: 10)
        let attributedCurrencyAmountString = NSMutableAttributedString(
            string: currencyAmountString,
            attributes: [.font: font!])
        if raisedDecimals == false {
            return attributedCurrencyAmountString as NSAttributedString
        }

        var array = attributedCurrencyAmountString.string.split(separator: ",")
        let lenght = array[0].count
        attributedCurrencyAmountString.setAttributes(
            [.font: fontSuper!, .baselineOffset: 10],
            range: NSRange(location: lenght, length: 3))
        attributedCurrencyAmountString.setAttributes(
            [.font: fontSuper!],
            range: NSRange(location: 0, length: 1))
        return attributedCurrencyAmountString as NSAttributedString
    }
}

答案 11 :(得分:0)

这是使用递归的Swift 5.1解决方案(也应与较早版本的Swift一起使用),该解决方案仅着重于从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] = [
        "⁰",
        "¹",
        "²",
        "³",
        "⁴",
        "⁵",
        "⁶",
        "⁷",
        "⁸",
        "⁹"
    ]

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

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

 func testPositiveIntegersSuperscript() {
        XCTAssertEqual(0.superscriptString(), "⁰")
        XCTAssertEqual(1.superscriptString(), "¹")
        XCTAssertEqual(2.superscriptString(), "²")
        XCTAssertEqual(3.superscriptString(), "³")
        XCTAssertEqual(4.superscriptString(), "⁴")
        XCTAssertEqual(5.superscriptString(), "⁵")
        XCTAssertEqual(6.superscriptString(), "⁶")
        XCTAssertEqual(7.superscriptString(), "⁷")
        XCTAssertEqual(8.superscriptString(), "⁸")
        XCTAssertEqual(9.superscriptString(), "⁹")
        XCTAssertEqual(10.superscriptString(), "¹⁰")
        XCTAssertEqual(11.superscriptString(), "¹¹")
        XCTAssertEqual(12.superscriptString(), "¹²")

        XCTAssertEqual(19.superscriptString(), "¹⁹")
        XCTAssertEqual(20.superscriptString(), "²⁰")
        XCTAssertEqual(21.superscriptString(), "²¹")

        XCTAssertEqual(99.superscriptString(), "⁹⁹")
        XCTAssertEqual(100.superscriptString(), "¹⁰⁰")
        XCTAssertEqual(101.superscriptString(), "¹⁰¹")
        XCTAssertEqual(102.superscriptString(), "¹⁰²")

        XCTAssertEqual(237.superscriptString(), "²³⁷")

        XCTAssertEqual(999.superscriptString(), "⁹⁹⁹")
        XCTAssertEqual(1000.superscriptString(), "¹⁰⁰⁰")
        XCTAssertEqual(1001.superscriptString(), "¹⁰⁰¹")

        XCTAssertEqual(1234.superscriptString(), "¹²³⁴")
        XCTAssertEqual(1337.superscriptString(), "¹³³⁷")
    }


    func testNegativeIntegersSuperscript() {
        XCTAssertEqual(Int(-1).superscriptString(), "⁻¹")
        XCTAssertEqual(Int(-2).superscriptString(), "⁻²")
        XCTAssertEqual(Int(-3).superscriptString(), "⁻³")
        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(), "⁻¹⁰")
        XCTAssertEqual(Int(-11).superscriptString(), "⁻¹¹")
        XCTAssertEqual(Int(-12).superscriptString(), "⁻¹²")

        XCTAssertEqual(Int(-19).superscriptString(), "⁻¹⁹")
        XCTAssertEqual(Int(-20).superscriptString(), "⁻²⁰")
        XCTAssertEqual(Int(-21).superscriptString(), "⁻²¹")

        XCTAssertEqual(Int(-99).superscriptString(), "⁻⁹⁹")
        XCTAssertEqual(Int(-100).superscriptString(), "⁻¹⁰⁰")
        XCTAssertEqual(Int(-101).superscriptString(), "⁻¹⁰¹")
        XCTAssertEqual(Int(-102).superscriptString(), "⁻¹⁰²")

        XCTAssertEqual(Int(-237).superscriptString(), "⁻²³⁷")

        XCTAssertEqual(Int(-999).superscriptString(), "⁻⁹⁹⁹")
        XCTAssertEqual(Int(-1000).superscriptString(), "⁻¹⁰⁰⁰")
        XCTAssertEqual(Int(-1001).superscriptString(), "⁻¹⁰⁰¹")

        XCTAssertEqual(Int(-1234).superscriptString(), "⁻¹²³⁴")
        XCTAssertEqual(Int(-1337).superscriptString(), "⁻¹³³⁷")
    }

由于我的数学和递归技术,我的解决方案的速度是gorillaz' solution(基于字符串和数组)的两倍以上。这是证明:

  private typealias SuperscriptVector = (value: Int, expectedSuperstring: String)
    private let vector1to9: SuperscriptVector = (123456789, "¹²³⁴⁵⁶⁷⁸⁹")

    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)

        }
    }

答案 12 :(得分:0)

 extension String {
    func convertToSuperscriptDigits(from start: Int, to end: Int? = nil) - String {

        let end = end ?? self.count
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(self.startIndex, offsetBy: end)
        let replaceRange = startIndex..<endIndex
        let substring = self[replaceRange]

        let supers = [
            "0": "\u{2070}",
            "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}"]

        let convertString = substring.map { (char) -> Character in
            Character(supers[String(char)] ?? String(char))
        }

        return self.replacingCharacters(in: replaceRange, with: String(convertString))
    }

答案 13 :(得分:0)

这将为字符串中的所有数字加上上标,并删除^字符。

使用:

yourstring.addSuper()

代码:

extension String {
    func addSuper() -> String {

        let charset = CharacterSet(charactersIn: "1234567890")

        let toSuper: [Character: String] = ["0": "\u{2070}",
                                            "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}",
                                            "-": "\u{207B}"]
        var resultString: String = ""

        var index: Int = 0

        for charater in self {
            if String(charater).rangeOfCharacter(from: charset) != nil {
                resultString.append(toSuper[charater] ?? "")
            } else if charater != "^" {
                resultString.append(charater)
            }
            index += 1
        }
        return resultString
    }
}

答案 14 :(得分:0)

对于使用SwiftUI的用户,一个选项是在Text()中使用Unicode异常字符串:

<div class="iRow">
     <div class="lclass"> 
          <label for="typeselector">Product Category</label> 
     </div>
     <div class="tclass"> 
          <select id="typeselector" name="productoptions">
               <option value="DVD">DVD-Disc</option>
               <option class="book" value="Book">Book</option>
               <option class="furniture" value="Furniture">Furniture</option>
          </select>
     </div>
</div>

此方法的一个优点是内联sub / supers更加容易。

如果它必须绝对继承自UILabel(例如,用于本机NSAttributedString或本机包装),则可以利用UIViewRepresentable并使用unicode异常字符串(在大多数情况下应该可以使用)。这是SO上的一个选项:Stackoverflow。我还没有尝试答案。

对于那些希望为通用下标和上标(例如算术)使用unicode的人:

上标:

0 = 2070

1 = 00B9

2 = 00B2

3 = 00B3

4 = 2074

5 = 2075

6 = 2076

7 = 2077

8 = 2078

9 = 2079

+ = 207A

-= 207B

(= 207D

)= 207E

n = 207F

下标:

0 = 2080

1 = 2081

2 = 2082

3 = 2083

4 = 2084

5 = 2085

6 = 2086

7 = 2087

8 = 2088

9 = 2089

+ = 208A

-= 208B

(= 208D

)= 208E

e = 2091

n = 2099

参考:unicode.org