LoginSignup
17
15

More than 5 years have passed since last update.

スクロールビューに動的に要素を生成・追加する(UIKit::UIScrollView)

Last updated at Posted at 2016-03-28

スタックビューに動的に要素を追加する

元ネタ

Single View Applicationでプロジェクトを作成

Project Nameは適当に

初期UI要素配置

  1. Scroll Viewを配置して制約を設定
    • TopをContrain to Marginにチェックを入れ、Top Layout Guideから0に設定(Scroll View.Top = Superview.TopMargin)
    • LeadingをContrain to Marginにチェックを入れ、Viewから0に設定(Scroll View.Leading = Superview.LeadingMargin)
    • TrailingをContrain to Marginにチェックを入れ、Viewから0に設定(Scroll View.Trailing = Superview.TrailingMargin)
    • BottomをContrain to Marginにチェックを入れ、Bottom Layout Guideから20に設定(Bottom Layout Guide.Top = Scroll View.Bottom + 20.0)
  2. Scroll ViewにVertical Stack Viewを配置して制約を設定
    • TopをContrain to Marginのチェックを外し、Scroll Viewから0に設定(Stack View.Top = Scroll View.Top)
    • LeadingをContrain to Marginのチェックを外し、Scroll Viewから0に設定(Stack View.Leading = Scroll View.Leading)
    • TrailingをContrain to Marginのチェックを外し、Scroll Viewから0に設定(Stack View.Trailing = Scroll View.Trailing)
    • BottomをContrain to Marginのチェックを外し、Scroll Viewから0に設定(Stack View.Bottom = Scroll View.Bottom)
    • Scroll ViewとStack Viewを選択し、Equal Widthsを設定(Stack View.Width = Scroll View.Width)
  3. Stack Viewの属性を設定
    • Axis = Vertical
    • Alignment = Fill
    • Distribution = Equal Spacing
    • Spacing = 0
  4. Stack ViewにButtonを配置し、Titleを「add item」に設定

Scroll ViewのOutletを作成

Outletとは、ストーリーボード上のオブジェクトの参照のこと(アウトレット - Swift による iOS 開発入門

  1. Xcode画面右上のShow the Assistant Editorを選択して、ViewController.swiftのコードを表示(右のUtilitiesは閉じておくと作業しやすいかもしれない)
  2. Scroll Viewを選択し、Controlを押しながらエディターのほうにドラッグしていき、以下の場所にドロップし、Connect後、privateに設定
ViewController.swift
class ViewController: UIViewController {
    //ここにドロップ
    override func viewDidLoad() {

操作方法は以下を参照
Swiftでの変数の定義、Xcodeでのアウトレット接続、iOSシミュレーターへの画像素材追加

Outletの設定

  • Connection = Outlet
  • Object = View Controller
  • Name = scrollView
  • Type = UIScrollView
  • Storage = Weak

コードを追加してprivateに

ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak private var scrollView: UIScrollView!

    override func viewDidLoad() {

Stack ViewのOutletを作成

Scroll Viewと同様に、以下のようにStack ViewのOutletを作成

ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak private var scrollView: UIScrollView!
    @IBOutlet weak private var stackView: UIStackView!

    override func viewDidLoad() {

Scroll Viewの初期スクロール位置設定

viewDidLoadを以下のように変更

ViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()

        // setup scrollview
        let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
        scrollView.contentInset = insets
        scrollView.scrollIndicatorInsets = insets
    }

補足

viewDidLoadは、ViewControllerクラスがインスタンス化された直後に呼び出されるメソッド
UIEdgeInsetsMakeはUIKitフレームワークに用意された関数で、ButtonやViewの周囲にマージンを設定

func UIEdgeInsetsMake(_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) -> UIEdgeInsets

contentInsetはUIScrollViewクラスのプロパティで、Scroll View内の要素のマージンを規定

var contentInset: UIEdgeInsets

scrollIndicatorInsetsはUIScrollViewクラスのプロパティで、スクロールバーのマージンを規定

var scrollIndicatorInsets: UIEdgeInsets

add itemボタンのAction作成

Controlを押しながらadd itemを以下の場所にドロップし、Connect

ViewController.swift
        scrollView.scrollIndicatorInsets = insets
    }

    // ここにドロップ

Actionの設定

  • Connection = Action
  • Object = View Controller
  • Name = addEntry
  • Type = AnyObject
  • Event = Touch Up Inside
  • Arguments = Sender

add itemボタンのAction設定

addEntryの中身を以下のように変更

ViewController.swift
    @IBAction func addEntry(sender: AnyObject) {

        let stack = stackView
        let index = stack.arrangedSubviews.count - 1
        let addView = stack.arrangedSubviews[index]

        let scroll = scrollView
        let offset = CGPoint(x: scroll.contentOffset.x, y: scroll.contentOffset.y + addView.frame.size.height)

        let newView = createEntry()
        newView.hidden = true
        stack.insertArrangedSubview(newView, atIndex: index)

        UIView.animateWithDuration(0.25) { () -> Void in
            newView.hidden = false
            scroll.contentOffset = offset
        }
    }

補足

arrangedSubviewsはUIStackView内の要素を格納したsubviewsのリスト

var arrangedSubviews: [UIView] { get }

CGPointはCore Graphicsフレームワークに用意された構造体で、二次元の座標を規定

struct CGPoint { var x: CGFloat var y: CGFloat init() init(x x: CGFloat, y y: CGFloat) }

contentOffsetはUIScrollViewクラスのプロパティで、Scroll View内での位置を規定

var contentOffset: CGPoint

insertArrangedSubviewはUIStackViewクラスのメッソドで、指定されたインデックスの位置にsubviewを追加

func insertArrangedSubview(_ view: UIView, atIndex stackIndex: Int)

animateWithDurationはUIViewクラスのメソッドで、Viewの変化のアニメーションを規定

class func animateWithDuration(_ duration: NSTimeInterval, animations animations: () -> Void)

削除機能追加

以下のコードを追加

ViewController.swift
            scroll.contentOffset = offset
        }
    }

    func deleteStackView(sender: UIButton) {
        if let view = sender.superview {
            UIView.animateWithDuration(0.25, animations: { () -> Void in
                view.hidden = true
                }, completion: { (success) -> Void in
                    view.removeFromSuperview()
                })
        }
    }

補足

removeFromSuperviewはUIViewのメソッドで、view自身を削除

func removeFromSuperview()

Entry Viewの中身作成

以下のコードを追加

ViewController.swift
                    view.removeFromSuperview()
                })
        }
    }

    private func createEntry() -> UIView {
        let date = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .NoStyle)
        let number = "\(randomHexQuad())-\(randomHexQuad())-\(randomHexQuad())-\(randomHexQuad())"

        let stack = UIStackView()
        stack.axis = .Horizontal
        stack.alignment = .FirstBaseline
        stack.distribution = .Fill
        stack.spacing = 8

        let dateLabel = UILabel()
        dateLabel.text = date
        dateLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)

        let numberLabel = UILabel()
        numberLabel.text = number
        numberLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)

        let deleteButton = UIButton(type: .RoundedRect)
        deleteButton.setTitle("Delete", forState: .Normal)
        deleteButton.addTarget(self, action: "deleteStackView:", forControlEvents: .TouchUpInside)

        stack.addArrangedSubview(dateLabel)
        stack.addArrangedSubview(numberLabel)
        stack.addArrangedSubview(deleteButton)

        return stack
    }

    private func randomHexQuad() -> String {
        return NSString(format: "%X%X%X%X",
            arc4random() % 16,
            arc4random() % 16,
            arc4random() % 16,
            arc4random() % 16
        ) as String
    }

NSDateFormatterはFoundationフレームワークのクラスで、NSDateオブジェクトを作成
localizedStringFromDateはNSDateFormatterクラスのメソッドで、指定されたフォーマットで、現在のロケールでの特定の日時を表す文字列を返す

class func localizedStringFromDate(_ date: NSDate,
                         dateStyle dstyle: NSDateFormatterStyle,
                         timeStyle tstyle: NSDateFormatterStyle) -> String

参考:NSDateFormatterStyleの種類

注意事項

  • 非表示のビューも、スタックが管理する配置対象ビューの配列に入っています。しかし画面には表示されず、他の配置対象ビューのレイアウトにも影響を及ぼしません。
  • あるビューを、スタック側の配置対象ビューの配列に追加すると、ビュー階層にも自動的に追加されます。
  • ビューをスタック側の配列から削除しても、ビュー階層から自動的に削除されることはありません。一方、ビュー階層からビューを削除すれば、スタック側の配列からも削除されます。
  • 通常、ビューのhiddenプロパティは、アニメーション可能(animatable、値を変更すると自動的にアニメーションが実行される)でありません。しかし、スタック側の配置対象ビューの配列にビューを置けば、アニメーション可能になります。実際にアニメーションを管理するのは、ビューではなくスタックです。hiddenプロパティを使って、ビューを追加/削除する際のアニメーション表示をしてください。
17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15