Подтвердить что ты не робот

Как использовать индекс и верхний индекс в Swift

Я хочу, чтобы мой UILabel отображал текст следующим образом: 6.022 * 10 23. Какие быстрые изменения имеют для индекса и надстрочного индекса?

4b9b3361

Ответ 1

Большинство ответов + примеры приведены в 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

Это дает мне:

SuperScript Example

В более подробном объяснении:

  • Получите UIFont, который вы хотите как для стиля по умолчанию, так и для надстрочного индекса, верхний индекс должен быть меньше.
  • Создайте NSMutableAttributedString с полной строкой и шрифтом по умолчанию.
  • Добавьте атрибут к символам, которые вы хотите изменить (NSRange), с меньшим/индексом UIFont, а значение NSBaselineOffsetAttributeName - это сумма, которую вы хотите скорректировать по вертикали.
  • Назначьте его UILabel

Надеюсь, это поможет другим разработчикам Swift, поскольку мне это тоже нужно.

Ответ 2

В качестве другого подхода я написал функцию, которая принимает строку, в которой экспоненты добавляются с помощью ^, например 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
}

Это немного метод грубой силы, но он работает, если вы не хотите иметь дело с атрибутированными строками или хотите, чтобы ваша строка не зависела от шрифта.

Ответ 3

Я написал следующее расширение, или вы можете использовать его как функцию, оно хорошо работает для меня. Вы можете изменить его, пропустив те части, которые вам не нужны

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

Ответ 4

Если вы можете ладить с текстом, который выглядит не идеально, и вам нужно только подмножество символов, вы можете использовать индексы индекса юникода и индексы: ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ Преимущество этого заключается в том, что он намного менее громоздкий.

Ответ 5

Для простого в использовании решения Swift вы можете проверить HandyUIKit. После импорта в свой проект (например, через Carthage - см. Инструкции в README) вы можете сделать что-то вроде этого:

import HandyUIKit

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

Эта строка вернет NSAttributedString, которая будет выглядеть точно как , что вы ищете. Просто присвойте ему свойству UILabel attributedText и , чтобы он!


Если вы ищете текст subscriptip, просто используйте subscripted(font:). Он распознает такие структуры, как CO_{2}. Там также superAndSubscripted(font:), если вы хотите объединить оба.

Дополнительную информацию и дополнительные примеры см. в docs.

Ответ 6

Swift 4+ Версия @Atka Ответ

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

Ответ 7

Вот простая версия, которая имеет правильную обработку ошибок и скомпилируется на игровой площадке.

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)

Ответ 8

Хорошая простая функция, которая выводит число в виде надстрочного текста.

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
}

Ответ 9

Я создал расширение 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

Я создал класс 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

Здесь решение 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 'решение(основанное на строках и массивах), потому что мое решение основано на математике и рекурсии. Вот доказательство:

  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 testPerformanceQaru() {
        measure {
            performanceTest(times: 1_000_000, function: Int.superscriptStringArrayBased)

        }
    }