スタックビューに動的に要素を追加する
Single View Applicationでプロジェクトを作成
Project Nameは適当に
初期UI要素配置
- 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)
- 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)
- Stack Viewの属性を設定
- Axis = Vertical
- Alignment = Fill
- Distribution = Equal Spacing
- Spacing = 0
- Stack ViewにButtonを配置し、Titleを「add item」に設定
Scroll ViewのOutletを作成
Outletとは、ストーリーボード上のオブジェクトの参照のこと(アウトレット - Swift による iOS 開発入門)
- Xcode画面右上のShow the Assistant Editorを選択して、ViewController.swiftのコードを表示(右のUtilitiesは閉じておくと作業しやすいかもしれない)
- Scroll Viewを選択し、Controlを押しながらエディターのほうにドラッグしていき、以下の場所にドロップし、Connect後、privateに設定
class ViewController: UIViewController {
//ここにドロップ
override func viewDidLoad() {
操作方法は以下を参照
Swiftでの変数の定義、Xcodeでのアウトレット接続、iOSシミュレーターへの画像素材追加
Outletの設定
- Connection = Outlet
- Object = View Controller
- Name = scrollView
- Type = UIScrollView
- Storage = Weak
コードを追加してprivateに
class ViewController: UIViewController {
@IBOutlet weak private var scrollView: UIScrollView!
override func viewDidLoad() {
Stack ViewのOutletを作成
Scroll Viewと同様に、以下のようにStack ViewのOutletを作成
class ViewController: UIViewController {
@IBOutlet weak private var scrollView: UIScrollView!
@IBOutlet weak private var stackView: UIStackView!
override func viewDidLoad() {
Scroll Viewの初期スクロール位置設定
viewDidLoadを以下のように変更
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
scrollView.scrollIndicatorInsets = insets
}
// ここにドロップ
Actionの設定
- Connection = Action
- Object = View Controller
- Name = addEntry
- Type = AnyObject
- Event = Touch Up Inside
- Arguments = Sender
add itemボタンのAction設定
addEntryの中身を以下のように変更
@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)
削除機能追加
以下のコードを追加
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の中身作成
以下のコードを追加
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
注意事項
- 非表示のビューも、スタックが管理する配置対象ビューの配列に入っています。しかし画面には表示されず、他の配置対象ビューのレイアウトにも影響を及ぼしません。
- あるビューを、スタック側の配置対象ビューの配列に追加すると、ビュー階層にも自動的に追加されます。
- ビューをスタック側の配列から削除しても、ビュー階層から自動的に削除されることはありません。一方、ビュー階層からビューを削除すれば、スタック側の配列からも削除されます。
- 通常、ビューのhiddenプロパティは、アニメーション可能(animatable、値を変更すると自動的にアニメーションが実行される)でありません。しかし、スタック側の配置対象ビューの配列にビューを置けば、アニメーション可能になります。実際にアニメーションを管理するのは、ビューではなくスタックです。hiddenプロパティを使って、ビューを追加/削除する際のアニメーション表示をしてください。