質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

928閲覧

uipickerviewのラベルが二重になる

manmo

総合スコア4

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/11/30 03:03

uipickerviewを用いてタイマーを作成しようとしています。数字の横に秒や分のラベルが固定されるようにしようとしたのですが、このlabelが以下の写真のように二重になってしまいます。
コードは「https://qiita.com/rin__/items/68b14247989ea1c0c47d」を参考にして下記のようにしました。
どうすれば二重になる状況を回避できるのか教えていただけると幸いです。よそしくお願いします。

swift

1 2import UIKit 3extension UIColor { 4 static let wb = UIColor(named: "wb")! 5 static let bw = UIColor(named: "bw")! 6} 7 8 9class ViewController: UIViewController,UIPickerViewDelegate, UIPickerViewDataSource { 10 11 @IBOutlet weak var cancel: UIButton! 12 @IBOutlet weak var start: UIButton! 13 14 @IBOutlet weak var timepicker: UIPickerView! 15 16 var hourlist = (0...23).map { String ($0) } 17 var minlist = (0...59).map { String ($0) } 18 var seclist = (0...59).map { String ($0) } 19 override func viewDidLoad() { 20 super.viewDidLoad() 21 22 cancel.titleLabel?.font = UIFont.systemFont(ofSize: 27) 23 cancel.layer.cornerRadius = 25 24 cancel.layer.borderColor = UIColor.bw.cgColor 25 cancel.layer.borderWidth = 1 26 27 28 start.titleLabel?.font = UIFont.systemFont(ofSize: 30) 29 start.layer.cornerRadius = 20 30 start.layer.borderWidth = 1 31 start.layer.borderColor = UIColor.bw.cgColor 32 33 timepicker.delegate = self 34 timepicker.dataSource = self 35 } 36 37 func numberOfComponents(in pickerView: UIPickerView) -> Int { 38 return 6 39 } 40 41 func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 42 43 switch component { 44 case 0: 45 return hourlist.count 46 case 1: 47 return 1 48 case 2: 49 return minlist.count 50 case 3: 51 return 1 52 case 4: 53 return seclist.count 54 case 5: 55 return 1 56 default: 57 return 0 58 } 59 60 } 61 62 func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 63 switch component { 64 case 0: 65 return hourlist[row] 66 case 1: 67 let hr = UILabel() 68 hr.text = "r" 69 timepicker.setPickerLabels(labels: [1: hr]) 70 71 return "" 72 case 2: 73 return minlist[row] 74 case 3: 75 let min = UILabel() 76 min.text = "min" 77 timepicker.setPickerLabels(labels: [3: min]) 78 return "" 79 case 4: 80 return seclist[row] 81 case 5: 82 83 let sec = UILabel() 84 sec.text = "sec" 85 timepicker.setPickerLabels(labels: [5: sec]) 86 87 return "" 88 default: 89 return "error" 90 } 91 } 92} 93 94extension UIPickerView { 95 func setPickerLabels(labels: [Int:UILabel]) { // [component number:label] 96 97 let fontSize:CGFloat = 20 98 let labelWidth:CGFloat = self.frame.size.width / CGFloat(self.numberOfComponents) 99 let x:CGFloat = self.frame.origin.x 100 let y:CGFloat = (self.frame.size.height / 2) - (fontSize / 2) 101 102 for i in 0...self.numberOfComponents { 103 104 if let label = labels[i] { 105 label.frame = CGRect(x: labelWidth * CGFloat(i), y: y, width: labelWidth, height: fontSize) 106 107 self.addSubview(label) 108 } 109 } 110 } 111} 112 113 114

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

このコードで表示しているPickerView(timerpicker)は、Storyboardに貼り付けてAutoLayoutやAutoresizingでレイアウトを設定して表示しているのだと思いますが、おそらくそのレイアウト設定で、表示するiPhoneの画面サイズに応じてPickerViewの大きさを変えるようにレイアウトしているのだろうと推測します。

その場合、ViewControllerを読み込んだ直後は一旦Storyboardに配置した時の大きさでPickerViewが読み込まれ、その後すぐにAutoLayout等のレイアウト処理が動作して、実際に表示するiPhoneの画面サイズにあった大きさに変更されます。

PickerViewに追加するLabelをどこで表示しているか、コードを確認したところ、 titleForRow のdelegateメソッドの中で、componentが1,3,5の場合に setPickerLabels を呼び出していますが、このdelegateメソッドは、ViewControllerを読み込んだ直後や、レイアウトを処理実施した後等、いろいろなタイミングで何度も呼び出されることがあります。

おそらく、ViewControllerを読み込んだ直後に titleForRow のdelegateメソッドが動作して、PickerViewが正しい大きさになる前に setPickerLabels を呼び出して、不正なサイズを元にした位置にLabelを追加し、その後、PickerViewが正しい大きさにレイアウトされた後 setPickerLabels を呼び出して正しいサイズを元にした位置にLabelを追加したため、ずれた位置に二重に表示されてしまったのだと思います。

とりあえず、 setPickerLabels の中でLabelを作成した際に

swift

1label.autoresizingMask = [ 2 .flexibleTopMargin, 3 .flexibleLeftMargin, 4 .flexibleRightMargin, 5 .flexibleBottomMargin, 6 .flexibleWidth, 7 .flexibleHeight]

このようにautoresizingMaskを設定しておけば、親のView(PickerView)の大きさが変わった時にLabelの表示位置と大きさが比例して追従するので二重に表示されることはなくなると思います。

さらに、 titleForRow のdelegateメソッドの中で setPickerLabels を呼び出すと、このdelegateが呼び出されるたびに何個もLabelが上乗せで追加されてしまいますので、ここで setPickerLabels を呼び出すのはやめて、viewDidLoadの中で次のように3つのLabelを作成して setPickerLabels を1度だけ呼び出すようにすれば、無駄なLabel上乗せも発生しなくなると思います。

swift

1let hr = UILabel() 2hr.text = "r" 3let min = UILabel() 4min.text = "min" 5let sec = UILabel() 6sec.text = "sec" 7timepicker.setPickerLabels(labels: [1: hr, 3: min, 5: sec])

投稿2021/11/30 06:27

TakeOne

総合スコア6299

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

manmo

2021/12/04 07:55

すみません。試してみたのですが、時間、分、秒のlabelそのものが表示されなくなってしまいました。 label.autoresizingMaskはどのように、どこに記述すれば良いのか教えていただけると助かります。
TakeOne

2021/12/04 11:00

label.frameの設定に続けて設定するのが適切だと思います。
manmo

2021/12/04 13:39

ありがとうございます。解決いたしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問