ios - tabla - Cómo usar subíndice y superíndice en Swift
superindice iphone (11)
Versión Swift 4+ de la respuesta de @ 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
}
}
Quiero que mi UILabel muestre texto de la siguiente manera 6.022 * 10 23 . ¿Qué funciones tiene swift para subíndice y superíndice?
Aquí hay una solución Swift 5.1 (también debería funcionar con versiones anteriores de Swift) usando recursividad, que solo se enfoca en la salida de un superíndice desde un
Int
(es decir, sin formato para mostrar).
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] } }
Aquí hay algunas pruebas para probar la corrección:
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(), "⁻¹³³⁷") }
Mi solución es más del doble de rápida que la solución de gorillaz (que se basa en cadenas y matrices), gracias a que la mía se basa en matemáticas y recursividad. Aquí está la prueba:
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 testPerformance() { measure { performanceTest(times: 1_000_000, function: Int.superscriptStringArrayBased) } }
Aquí hay una versión simple que tiene un manejo correcto de errores y se compilará en el patio de recreo.
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)
Como un enfoque diferente, escribí una función que toma una cadena donde los exponentes se anteponen con
^
como
2^2•3•5^2
y devuelve
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
}
Es un poco un método de fuerza bruta, pero funciona si no desea tratar con cadenas atribuidas o si su cadena es independiente de una fuente.
Creé una clase AmountFormatter que me ayudó a convertir números decimales en números con decimales elevados.
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
}
}
Escribí la siguiente extensión o puedes usarla como una función, me está funcionando bien. puedes modificarlo saltando las partes que no son esenciales para ti
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}
}
ejemplos:
let attStr1 = NSMutableAttributedString().characterSubscriptAndSuperscript(
string: "23 x 456",
characters:["3","5"],
type: .aSuper,
fontSize: 20,
scriptFontSize: 15,
offSet: 10,
length: [1,2],
alignment: .left)
let attStr2 = NSMutableAttributedString().characterSubscriptAndSuperscript(
string: "H2SO4",
characters: ["2","4"],
type: .aSub,
fontSize: 20,
scriptFontSize: 15,
offSet: 8,
length: [1,1],
alignment: .left)
He creado una extensión de cadena que toma una cadena y convierte todo su superíndice en caracteres unicode. De esta manera, por ejemplo, podría compartir la cadena resultante sin problemas.
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
}
}
Por ejemplo,
"x^{4+n}+12^{3}".superscripted()
produce
"x⁴⁺ⁿ+12³"
Esto fue inspirado por HandyUIKit y la esencia de mi código está en Github
La mayoría de las respuestas + ejemplos están en ObjC, pero así es como hacerlo en 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
Esto me da:
En una explicación más detallada:
-
Obtenga
UIFont
que desee tanto para el estilo predeterminado como para el superíndice, el superíndice debe ser más pequeño. -
Cree un
NSMutableAttributedString
con la cadena completa y la fuente predeterminada. -
Agregue un atributo a los caracteres que desea cambiar (
NSRange
), con elUIFont
más pequeño / subíndice, y el valorNSBaselineOffsetAttributeName
es la cantidad que desea compensar verticalmente. -
Asignarlo a tu
UILabel
Espero que esto ayude a otros desarrolladores de Swift, ya que también necesitaba esto.
Para una solución Swift fácil de usar , es posible que desee pagar HandyUIKit . Después de importarlo a su proyecto (por ejemplo, a través de Cartago, consulte las instrucciones en README) puede hacer algo como esto:
import HandyUIKit
"6.022*10^{23}".superscripted(font: UIFont.systemFont(ofSize: 20, weight: .medium))
Esta línea devolverá un
NSAttributedString
que se verá exactamente como
lo que está buscando
.
¡Solo tiene que
asignarlo
a la propiedad
attributedText
UILabel
y
UILabel
!
Si está buscando
suscribir
un texto, simplemente use
subscripted(font:)
lugar.
Reconocerá estructuras como
CO_{2}
.
También hay
superAndSubscripted(font:)
si quieres combinar
ambos
.
Consulte los docs para obtener más información y ejemplos adicionales.
Si puede llevarse bien con el texto que no se ve perfecto, y solo necesita un subconjunto de caracteres, puede utilizar los números de superíndice y subíndice Unicode: ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ Esto tiene la ventaja de ser mucho menos engorroso.
Una función simple y agradable que genera un número como texto de superíndice.
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
}