[Swift] StoryBoardを使わず画面を作る

StoryBoardの使い方(というか細かい設定)がよく分からず、コーディングで全部作ってやる! ってことで、画面表示まで作ってみました。(iOS用です)









プロジェクト作成

とりあえず、プロジェクトは「Single View App」を選択。
自動で作成されるStoryBoardなど、不要なものは削除しますし、少ない方が良い!
ってことで。

削除したファイル
・Main.storyboard
・LaunchScreen.storyboard

そして、プロファイル定義で、StoryBoardを設定している箇所を変更




ファイルを追加

ViewController

自動生成されているViewControllerの名前をBasicSettingViewController変更して、コーディングを追加。



BasicSettingViewController.swift
import UIKit

class BasicSettingViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let basicView = BasicSettingView(frame: self.view.bounds)
        
        basicView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        self.view.addSubview(basicView)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    }    
}

画面の定義(オブジェクト類)は、viewDidLoad()の中に書いても良かったのだけど、 オブジェクトの数が多くなったり、複雑な画面を作ろうとした場合、外出しにした方が見やすいかな〜 と思い、BasicSettingViewを作成して組み込んでみました。

BasicSettingView.swift
import UIKit

class BasicSettingView: UIView {
    //
    let arrayRoundUnit = ["None","10","100","1,000"]
    let arrayRounding  = ["Over","Short"]

    // Screen Object
    let lblRoundUnit = UILabel()
    var sgcRoundUnit = UISegmentedControl()
    let lblRounding  = UILabel()
    var sgcRounding  = UISegmentedControl()

    // 初期化
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        buildView()
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func buildView(){
        
        self.backgroundColor = UIColor.DefaultBGColor
        // ラベル
        lblRoundUnit.textColor = UIColor.darkText
        lblRoundUnit.text = "Round Unit"
        lblRoundUnit.textAlignment = .left
        self.addSubview(lblRoundUnit)
        
        // セグメントコントロール
        sgcRoundUnit = UISegmentedControl(items: arrayRoundUnit as [AnyObject])
        self.addSubview(sgcRoundUnit)

        // ラベル
        lblRounding.textColor = UIColor.DefaultText
        lblRounding.text = "Rounding"
        lblRounding.textAlignment = .left
        self.addSubview(lblRounding)

        // セグメントコントロール
        sgcRounding = UISegmentedControl(items: arrayRounding as [AnyObject])
        self.addSubview(sgcRounding)

    }
    
    override func layoutSubviews() {
        super.layoutSubviews()

        let spacing = CGFloat(8)
        let x = spacing
        var y = spacing
        var pos = CGPoint(x: x, y: y)
        
        //
        var sz = self.lblRoundUnit.sizeThatFits(self.bounds.size)
        sz.width  = (self.bounds.width - (spacing * 2))
        self.lblRoundUnit.frame = CGRect(origin: pos, size: sz)

        //
        var itemWidth = CGFloat( self.bounds.width - spacing * 2 ) / 4.0
        for (i,_) in arrayRoundUnit.enumerated() {
            self.sgcRoundUnit.setWidth(itemWidth, forSegmentAt: i)
        }
        y = y + sz.height + spacing
        sz = self.sgcRoundUnit.sizeThatFits(self.bounds.size)
        pos = CGPoint(x: self.bounds.width / 2, y: y + sz.height / 2)
        self.sgcRoundUnit.center = pos
        
        //
        y = y + sz.height + spacing
        pos = CGPoint(x: x, y: y)
        sz = self.lblRounding.sizeThatFits(self.bounds.size)
        sz.width  = (self.bounds.width - (spacing * 2))
        self.lblRounding.frame = CGRect(origin: pos, size: sz)

        //
        itemWidth = CGFloat( self.bounds.width - spacing * 2 ) / 2.0
        for (i,_) in arrayRounding.enumerated() {
            self.sgcRounding.setWidth(itemWidth, forSegmentAt: i)
        }
        y = y + sz.height + spacing
        sz = self.sgcRounding.sizeThatFits(self.bounds.size)
        pos = CGPoint(x: self.bounds.width / 2, y: y + sz.height / 2)
        self.sgcRounding.center = pos

    }
    
}

init() をオーバーライドして画面オブジェクトを定義

ラベルの色は、UIColorを拡張して定義してます。 こうすると、後で一括で色を変えたい場合も一箇所修正するだけで対応できるので便利かな〜と思います。 拡張関連は1、別ファイルでまとめてます。

Extensions.swift
// 色拡張
extension UIColor {
    static let DefaultBGColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1)
    static let DefaultText    = UIColor(red: 0.1, green: 0.1, blue: 0.2, alpha: 1)
}

layoutSubviews()をオーバーライドして、レイアウトを調整 

ラベルのレイアウトは、frameに(x,y)の位置と、(width,height)のサイズを渡して設定してます。
セグメントコントロールのレイアウトは、centerで中心位置を渡してます。
幅は、表示セグメントの幅と数に依存するので、setWidthで各セグメントの幅を計算して設定します。

あとは、AppDelegateを修正。
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.rootViewController = BasicSettingViewController()
        window?.makeKeyAndVisible()
        
        return true
    }

これで、テスト実行すると、画面が表示されます。
まだベタ書きが多くて、美しくないソースなので、のんびりと綺麗に仕上げていきたいと思います。

おまけ

extensionってホント便利ですね。
このクラスにこんなメソッドがあれば便利なのに!
という要望に、比較的簡単に対応できる。

日本円の金額表示関連の処理は、拡張したら便利になりました。

// Int型の拡張
extension Int {
    // Intを3桁カンマ表記の文字列に変換
    var decimalStr: String {
        let decimalFormatter = NumberFormatter()
        decimalFormatter.numberStyle = NumberFormatter.Style.decimal
        decimalFormatter.groupingSeparator = ","
        decimalFormatter.groupingSize = 3
        return decimalFormatter.string(from: self as NSNumber)!
    }
}

// String型の拡張
extension String {
    // カンマ区切りの文字列をIntに変換
    // 変換できなかった場合は0を返す
    var decimalInt: Int {
        let tmp = self
        let temp = tmp.replacingOccurrences(of: ",", with: "")
        if let tmpInt = Int(temp) {
            return tmpInt
        } else {
            return 0
        }
    }
}

これでInt型に代入した数値をdecimalStr()で3桁カンマ区切りの文字列に変換してくれるし、 String型に代入されているカンマ区切り数値をInt型に変換できる。
ちょっとした事ですが、おぉ!! ってなりました(笑

 金額をIntにしてたら、USDとか扱えないんじゃない!? とか
実はもっと良い方法があるのに・・・ とか
色々あるかもしれませんけどね。

コメント

このブログの人気の投稿

[Music] DTM初心者のためのドラム打ち込み その2

[Swift] StoryBoardを使用しない - UITextFieldで編集不可にする方法

[Swift] UISliderをカスタマイズしてみる