概要
TwitterでフォロワーさんがUIStackViewで挑戦してたのを見かけた。
あえて同じくStackViewで挑戦して¥が下はみ出る… https://t.co/0rPgMqLocr
— ありぜ (@a_aryzae) 2018年9月7日
スレッドを遡ってみると、NSAttributedStringで実装できるんじゃないかという話があり、興味が湧いたので挑戦してみた。
作ったもの
文字のサイズ感は目見当。

//
// ViewController.swift
// Price
//
// Created by macneko on 2018/09/07.
// Copyright © 2018年 macneko. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
self.view.addSubview(label)
label.frame = self.view.frame
label.textAlignment = .center
let fontSize: CGFloat = 80
let font = UIFont.systemFont(ofSize: fontSize)
let attributeText = NSMutableAttributedString(string: "各種¥(税込)280",
attributes: [.font: font, .foregroundColor: UIColor.red])
// 各種
attributeText.addAttributes([.font: UIFont.systemFont(ofSize: fontSize * 0.4),
.baselineOffset: 2],
range: NSRange(location: 0, length: 2))
// ¥
attributeText.addAttributes([.font: UIFont.systemFont(ofSize: fontSize * 0.6),
.kern: -50],
range: NSRange(location: 2, length: 1))
// (税込)
attributeText.addAttributes([.font: UIFont.systemFont(ofSize: fontSize * 0.2),
.baselineOffset: (fontSize * 0.8) - (fontSize * 0.2) - 4],
range: NSRange(location: 3, length: 4))
// ()のベースライン調整
attributeText.addAttributes([.baselineOffset: (fontSize * 0.8) - (fontSize * 0.2) - 3],
range: NSRange(location: 3, length: 1))
attributeText.addAttributes([.baselineOffset: (fontSize * 0.8) - (fontSize * 0.2) - 3],
range: NSRange(location: 6, length: 1))
label.attributedText = attributeText
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
説明
ソースコードを部分的に切り出して処理内容を説明する。
let fontSize: CGFloat = 80
let font = UIFont.systemFont(ofSize: fontSize)
let attributeText = NSMutableAttributedString(string: "各種¥(税込)280",
attributes: [.font: font, .foregroundColor: UIColor.red])
はじめに attributeText
に基本的な装飾と文字列を格納しておいて、装飾を変更したい部分をNSRangeで指定して、加工していく方法をとることにした。
// 各種
attributeText.addAttributes([.font: UIFont.systemFont(ofSize: fontSize * 0.4),
.baselineOffset: 2],
range: NSRange(location: 0, length: 2))
各種 の部分。
文字サイズは基本サイズの40%の大きさを指定。
¥
や数字よりベースラインが少し下にずれて見えるので、.baselineOffset: 2
を指定して少し上にずらした。
ベースラインについては ラテン文字と欧文組版 をP14の図を参照。
雑に説明すると、.baselineOffset
に正の数を与えると上に移動し、負の数を与えると下に移動する。
// ¥
attributeText.addAttributes([.font: UIFont.systemFont(ofSize: fontSize * 0.6),
.kern: -50],
range: NSRange(location: 2, length: 1))
¥ の部分。
文字サイズは基本サイズの60%の大きさを指定。
.kern: -50
を指定して、次の文字の ( との字間を詰めた。
雑に説明すると、.kern
(カーニング)は当該文字と次の文字の文字間のアキを詰める設定。
なお、パーレンにはカーニングが適用されないっぽかったので、¥を前にもってきてカーニング設定をしている。
// (税込)
attributeText.addAttributes([.font: UIFont.systemFont(ofSize: fontSize * 0.2),
.baselineOffset: (fontSize * 0.8) - (fontSize * 0.2) - 4],
range: NSRange(location: 3, length: 4))
(税込) の部分。
文字サイズは基本サイズの20%の大きさを指定。
ベースラインは相対値で計算したものの、それだけではうまく揃わなかったので、微調整している。
// ()のベースライン調整
attributeText.addAttributes([.baselineOffset: (fontSize * 0.8) - (fontSize * 0.2) - 3],
range: NSRange(location: 3, length: 1))
attributeText.addAttributes([.baselineOffset: (fontSize * 0.8) - (fontSize * 0.2) - 3],
range: NSRange(location: 6, length: 1))
( と ) の部分。
ベースラインが揃わなかったので、この部分だけさらに微調整した。
文字列ごとにNSAttributedStringKeyを指定していき、最後に結合する方法でも良かったんだけど、DTP的な手法のほうが手慣れているため、この方法にしてしまったがゆえに、すごくIllustratorやInDesignを触っている気分になった。
まとめ
奥さんに「できたで!」って誇らしげに見せたら「あ、はい」という感想をいただいた。iOSでもチラシレイアウトできるんやで!すごくない?!すごいやろ?! https://t.co/7VlaDJlVyc
— こうちゃん黒猫まみれ (@macneko_ayu) 2018年9月7日