【Xcode】UILabelの文字サイズを自動調整する
画面サイズに合わせてUILabelが拡大縮小する場合に、文字サイズも自動調整する方法。
何もしないとどうなるか
サンプルとして、5.5inch(ex. iPhone8 Plus)の画面サイズに合わせてレイアウトを作成しました。
青い四角部分がUILabelで、画面サイズに合わせてサイズが変わります。
まず iPhone8 Plusで表示した場合。問題ありません。
次にiPhone4sで表示した場合。文字が省略されてしまいました。
これを調整していきます。
1. Autoshrinkを有効にする
UILabelにはAutoshrinkという、UILabelのサイズに合わせて文字サイズを自動調整してくれるそのものずばりな機能があります。
Storyboardから設定する場合。
「Autoshrink」を「Minimum Font Scale」にし、下の欄でScaleを指定します。
Scaleは、自動調整時の文字サイズ最小値を、元々の文字サイズに対する比率で入力します。
コードから設定する場合。
label.adjustsFontSizeToFitWidth = true label.minimumScaleFactor = 0.3
文字サイズが自動調整されました。
ただ、これだとラベルぎりぎりすぎるので、余白をつけたい場合は次に進みます。
2. drawTextで余白(padding)を付ける
UILabelのカスタムクラスを作成し、下記のようにdrawTextを実装します。
class AutoShrinkLabel: UILabel { var padding: UIEdgeInsets = UIEdgeInsetsMake(4, 4, 4, 4) override func drawText(in rect: CGRect) { let newRect = UIEdgeInsetsInsetRect(rect, padding) super.drawText(in: newRect) } }
Storyboardからラベルに設定します。
余白がつきました。
大体これでいけるのですが、この方法は縦方向に余白を大きく取りたい場合に上手くいかないことがあります。
おそらく「label.adjustsFontSizeToFitWidth」というプロパティ名の通り、幅に合わせて自動調整しているため。
3. viewDidLayoutSubviewsで余白(padding)を付ける
drawTextでは調整しきれない場合は、UIViewControllerでviewDidLayoutSubviewsを実装して自力で計算します。
こうなるとdrawTextはなくてもいいかも。
class AutoShrinkLabel: UILabel { var padding: UIEdgeInsets = UIEdgeInsetsMake(20, 4, 20, 4) } class ViewController: UIViewController { @IBOutlet weak var label: AutoShrinkLabel! override func viewDidLayoutSubviews() { let fontSize = label.frame.size.height - (label.padding.top + label.padding.bottom) label.font = label.font.withSize(fontSize) } }
以上です!
【Xcode】iOS11からUITableViewのSwipe Actionが新しくなった
iOS11からUITableViewDelegateにSwipe Actionの新しいメソッドが追加されました。
これによって以下のことができるようになりました。
(iOS11以降限定です)
- 左から右へのSwipe Actionを実装する
- Swipe Actionに画像を表示する
使用イメージ
左から右へのSwipe Action使用例です。
こんな感じで文字の代わりにアイコンを表示できるようになりました!
こちらは従来通り右から左へのSwipe Action、文字を表示した場合です。
こちらもアイコンに変えることができます。
サンプルコード
上記の使用イメージで使用したソースコードです。
storyboardで適当なUITableViewControllerと紐づけてください。
class ViewController: UITableViewController { let dataSource = ["One", "Two", "Three"]; override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataSource.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") cell.textLabel?.text = dataSource[indexPath.row] return cell } // iOS11以降 // 右から左へスワイプ @available(iOS 11.0, *) override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let editAction = UIContextualAction(style: .normal, title: "Edit", handler: {(action: UIContextualAction, view: UIView, completion: (Bool) -> Void) in print("Edit") // 処理を実行完了した場合はtrue completion(true) }) editAction.backgroundColor = UIColor(red: 101/255.0, green: 198/255.0, blue: 187/255.0, alpha: 1) let deleteAction = UIContextualAction(style: .destructive, title: "Delete", handler: { (action: UIContextualAction, view: UIView, completion: (Bool) -> Void) in print("Delete") // 処理を実行できなかった場合はfalse completion(false) }) deleteAction.backgroundColor = UIColor(red: 214/255.0, green: 69/255.0, blue: 65/255.0, alpha: 1) return UISwipeActionsConfiguration(actions: [editAction, deleteAction]) } // iOS11以降 // 左から右へスワイプ @available(iOS 11.0, *) override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let favoriteAction = UIContextualAction(style: .normal, title: "Favorite", handler: { (action: UIContextualAction, view: UIView, completion: (Bool) -> Void) in print("Favorite") // 処理を実行完了した場合はtrue completion(true) }) favoriteAction.backgroundColor = UIColor(red: 210/255.0, green: 82/255.0, blue: 127/255.0, alpha: 1) favoriteAction.image = UIImage(named: "ic_favorite") return UISwipeActionsConfiguration(actions: [favoriteAction]) } }
『マジ文章書けないんだけど~朝日新聞ベテラン校閲記者が教える一生モノの文章術~』
書店員の友人から「タイトルの割にしっかりした内容で、ためになる」とおすすめされた本。
「マジ文章書けないんだけど」と言うほどひどくはない、…と思いたいけれど、
私も文章が上手く書けないと悩むことが多いので読んでみました。
内容の紹介
就活を控えた女子大生・すずが、文章の達人である謎のおじさんに文章の書き方を教えてもらうストーリー仕立てになっています。
最初は簡単な文法の説明から、だんだんと文章の膨らませ方、エントリーシートの書き方へと進みます。
就活生向けの内容と思いきや、就活に限定した内容は最後の15ページほどなので、意外と万人向けでした。
くだけた会話調で書かれ、可愛いイラストも多いので、本を読むのが苦手という人には読みやすいと思います。
この本が紹介する「文章術」の半分は、
- 助詞の「は」と「が」の違い
- 主語と述語を対応させる
- こそあど言葉
など、とても基本的な文法の説明です。
もう半分は
- 一つの文に一つの内容
- 5W1H、特にWhyが重要
- 結論から先に書く
など、これもまた一度は聞いたことがあるような基本的な内容がほとんど。
タイトル通り「マジ文章書けないんだけど」な人にわかりやすい文章の書き方、書き始め方を教える本なので、ある程度勉強している人には物足りないかもしれません。
私もほとんど聞いたことのある内容ではありましたが、改めて振り返ると結構できていないもので。
文筆のプロのような上手い文章が書けるテクニックの紹介を期待していたので、そういう意味では期待はずれでしたが、上部のテクニックや色気を出す前に、基礎基本をきちんとすることが大事ですね。
この本の良いところは、本当にありそうなダメな文章の例を出して、ダメな原因と改善例が説明されているところです。
例えば
「このバックはブランドものなので、値段と人気が高く、品質とデザインが美しいブランドだ」
という文例。
自分もこんな文を書いている、ダメなのはわかるが何がダメでどう直せばいいのか説明できないという人には一読の価値ありです。
個人的に特に参考になったのは以下のアドバイスです。
- 一つの文に一つの内容
- 「だが」と「ので」などのつなぎ言葉はできるだけ使わない
私は一つの文にいろいろ詰め込んで文が長くなりがちで、結局何が言いたいかが迷子になることがよくあります。
この本を読んでから、意識的に文を短くシンプルにするようになり、少し良くなった実感があります。
文を短くした方が書くのも早いですね。
書かれている内容が根本的であるだけに実践しやすく、普遍的に使える”一生モノの文章術”だと思いました。
【Xcode】XCUITest + fastlane/snapshotで始めるUIテスト(後編)
後編ではfastlane/snapshotを導入して、任意のタイミングでスクリーンショットを取得します。
前編はこちらから。
【Xcode】XCUITest + fastlane/snapshotで始めるUIテスト(前編) - 文系プログラマの勉強ノート
fastlane/snapshotとは
fastlaneは、iOS/Androidアプリのリリース作業を自動化するツール群です。
そのうちの一つであるsnapshotを使うと、UIテストをしながらストア申請時のスクリーンショットを自動作成できます。
開発環境
- Mac OS Sierra
- Xcode8.3
- Swift3
導入手順
1.Xcode command line toolsをインストールする
ターミナルを起動し、Xcode command line toolsをインストールします
$ xcode-select --install
2.fastlaneをインストールする
ターミナルからRubygemsを使ってインストールします。
この他、Homebrew、Zipで入れる方法もあります
$ sudo gem update $ sudo gem install fastlane -NV
3.snapshotを初期化する
snapshotを取りたいアプリの .xcodeproj ファイルがあるディレクトリに移動し、下記のコマンドを実行します。
$ cd /Users/xxx/XCUITestSample $ fastlane snapshot init $ sudo gem install fastlane -NV
下記のファイルが生成されます。
- Snapfile
- SnapshotHelper.swift
4.SnapshotHelper.swiftをプロジェクトに追加する
Xcodeを起動し、生成されたSnapshotHelper.swift をUIテストターゲットに追加します。
SnapshotHelper.swift は移動可なので、私はファイル本体も UIテストディレクトリ以下に移動しました。
5.テストコードを編集する
前編で作成した ***UITests.swift の setUp() を修正します。
//XCUIApplication().launch()
let app = XCUIApplication()
setupSnapshot(app)
app.launch()
UIテストコード(ここではtestExample())のスクリーンショットを取得したい箇所に下記のコードを追加します。
""の文字列は任意の文字列です。画像ファイル名に使用されますので、重複しないようにします。
snapshot("ScreenShot_01")
6.Snapfileを編集する
スクリーンショットを取得したい端末と言語をSnapfileに記載します。
以下は iPhone7 の日本語の場合です。
devices([ "iPhone 7", "iPhone 7 Plus", ]) languages([ "en-US", "ja-JP", ])
7.fastlane/snapshotを実行する
ターミナルから下記のコマンドでfastlane/snapshotを実行します。
(snapshotを取りたいアプリの .xcodeproj ファイルがあるディレクトリで実行してください)
$ fastlane snapshot
設定ファイルやテストコードに問題がなければ、自動的にシミュレータが起動し、テストが実行されます。
完了すると screenshots/screenshots.html が表示されます。
screenshots/screenshots.html には作成されたスクリーンショットが環境別に一覧になっています。
画像は screenshots ディレクトリ以下に保存されます。
公式ドキュメントによると、ストア申請用にデバイスフレームをつけたりもできるようです。
参考URL
【Xcode】XCUITest + fastlane/snapshotで始めるUIテスト(前編)
前編では、XCUITestを導入してUITestを実行してみます。
XCUITestとは
Xcode7から追加されたUIテスト機能です。
UIテストをするためのフレームワークはEarlGreyやappiumなどもありますが、
iOS8以降対応で問題なければ、XCUITestが扱いやすいと思います。
開発環境
- Mac OS Sierra
- Xcode8.3
- Swift3
導入手順
1. UI Testing Bundleを追加する
UITestを追加したいプロジェクトを開き、メニューの File > New > Target... を選択します。
iOS > Test > "iOS UI Testing Bundle" を選択して Next を押します。
Product Name を入力して Finish を押します。
(新規プロジェクトの場合は、作成時にプロジェクト名などを入力する画面で "Inclure UI Tests" にチェックを入れればOK)
2. テスト対象となる機能を作成する(すでにある場合は次へ)
今回はテスト用なので、テキストフィールドに文字を入力して決定ボタンを押すと
入力内容がアラートで表示される機能にしました。
3. Accessibility identiferを設定する(すでにある場合は次へ)
テストコードからコントロールにアクセスするために、Accessibility identiferを設定します。
コードから追加する方法もありますが、ここではIBを開いてコントロールを選び、右側のペインの
Identity inspector(左から3番目) > Accessibility > identifer
に一意の文字列を設定します。
※Accessibility identiferが保存されない場合※
稀にAccessibility identiferを設定しても、実行時に保存されていないことがあります。
その場合、Accessibility identifer設定後に、座標やサイズなどレイアウト情報を変更すると保存されました。
(おそらくXcodeのバグ。保存されたあとはレイアウト情報は戻して大丈夫です。)
4. テストコードを作成する
先程追加したUITestTarget内のswiftファイルにテストコードを書きます。
(デフォルトでは"[ProjectName]UITests.swift"というファイル名になっていると思います。)
初期状態で「testExample()」というメソッドが作成されていますが、
このように「test〜」で始まるメソッドがテスト時に実行されるコードになります。
testExample()内にテストコードを作成します。
"textField"、"decisionButton"は先程設定したAccessibility identiferです。
func testExample() { let app = XCUIApplication() // 文字を入力 let textField = app.textFields["textField"] textField.tap() textField.typeText("UIテストを実行") // 決定ボタンをタップ let button = app.buttons["decisionButton"] button.tap() // 入力内容がアラートに表示される }
5. テストを実行する
メニューの Product > Test を選択するか、テストメソッド名の左側の◇ボタンを押してテストを実行します。
◇ボタンが緑色になれば成功、赤くなれば失敗です。
【Xcode】コードだけでグラデーション作成
下図のようなグラデーションをコードだけで作る方法です。
override func viewDidLoad() { super.viewDidLoad() let colors = [UIColor(red: 112/255, green: 134/255, blue: 241/255, alpha: 1.0).cgColor, UIColor(red: 40/255, green: 169/255, blue: 255/255, alpha: 1.0).cgColor, UIColor(red: 180/255, green: 225/255, blue: 255/255, alpha: 1.0).cgColor] let gradientLayer = gradientLayerWith(frame: view.bounds, colors: colors) view.layer.addSublayer(gradientLayer) } func gradientLayerWith(frame: CGRect, colors: Array<Any>) -> CAGradientLayer { let layer = CAGradientLayer() layer.frame = frame layer.colors = colors // グラデーションの向きを横にしたい場合 layer.startPoint = CGPoint(x: 0, y: 0.5) layer.endPoint = CGPoint(x: 1, y: 0.5) return layer }
【Xcode】UIAlertControllerで簡単進捗ダイアログ作成
カスタムビューを作らず、UIAlertControllerで簡単な進捗ダイアログを表示する方法です。
// インジケータ表示 alert = UIAlertController(title: "Loading...", message: "\n", preferredStyle: .alert) let indicator = UIActivityIndicatorView() indicator.translatesAutoresizingMaskIntoConstraints = false alert.view.addSubview(indicator) let views: [String: UIView] = ["alert": alert.view, "indicator": indicator] var constraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[indicator]-(12)-|", options: [], metrics: nil, views: views) constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[indicator]|", options: [], metrics: nil, views: views) alert.view.addConstraints(constraints) indicator.isUserInteractionEnabled = false indicator.color = UIColor.lightGray indicator.startAnimating() present(alert, animated: true, completion: { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.alert.dismiss(animated: true, completion: { self.alert = nil }) } })
こちらのサイトの回答を参考にSwift3.0の文法で書き直し、インジケータの位置を調整しました。
アラートの高さをmessageの改行で調整しているのが少々ださいですが、
カスタムビューを作らなくて良いのでお手軽ですね。
stackoverflow.com