アニメーション実施後、autoLayoutの制約が重複する
初めて投稿いたします。
当方autoLayoutがよく理解できておらず、皆様のお力をお借りしたいと思い投稿いたしました。
現在テスト的に以下の動作をするアプリを作っています。
<テスト環境>
iOS12.1.4
xcode 10.1
swift 4.2
<動作内容>
1:2つのビューがある(添付コードでは緑、赤)
2:タップすると画面一杯のサイズに拡大される
3:閉じるを押すと、元のサイズに戻る
4:ビュー拡大時にスワイプすると、ビューは横移動(この時もう一方を移動前に拡大)
というサンプルなのですが
4のもう一方のビューの閉じるを押しても、元のサイズに戻りません。
デバッグしていると4にて閉じるを押した時
ViewController.viewへの制約に
拡大中の制約が2つ重複している現象が発生していました。
なぜこのような現象が発生するのか、どのように修正すればいいのか分かっておりません。
どなたかご教授して頂きたいと思います。
サンプルコードですが以下に全文を乗せさせて頂きます。
何卒よろしくお願い致します。
import UIKit
class ViewController: UIViewController, CustomViewDelegate {
// 赤ビュー
lazy var red: CustomView = CustomView.init(frame: CGRect.zero)
// 緑ビュー
var green: CustomView = CustomView.init(frame: CGRect.zero)
// 保持用
var saveCon : [NSLayoutConstraint] = []
var conGreen : [NSLayoutConstraint] = [] // 緑ビュー制約
var conRed : [NSLayoutConstraint] = [] // 赤ビュー制約
// 拡大
func scaleUp(upView: CustomView) {
// 最前面に移動
self.view.bringSubviewToFront(upView)
// 赤ビューの制約無効
if upView == red{
NSLayoutConstraint.deactivate(conRed)
}
// 緑ビューの制約無効
else{
NSLayoutConstraint.deactivate(conGreen)
}
// 新規制約生成(画面いっぱいに広げる)、有効にする
let constA = upView.topAnchor.constraint(equalTo: view.topAnchor)
let constB = upView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
let constC = upView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
let constD = upView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
// 作成した制約保持(縮小時に使用するため)
saveCon = [constA, constB, constC, constD]
NSLayoutConstraint.activate(saveCon)
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
}, completion: { finished in
})
}
// 縮小
func scaleDown(downView: CustomView) {
// 保持していた制約を使用して無効にする
NSLayoutConstraint.deactivate(saveCon)
// 赤ビューの元々の制約有効
if downView == red{
NSLayoutConstraint.activate(conRed)
}
// 緑ビューの元々の制約有効
else{
NSLayoutConstraint.activate(conGreen)
}
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
}, completion: { finished in
})
}
func swipeDirection(sender: UISwipeGestureRecognizer){
let notSwipeView = sender.view != red ? red : green
// スワイプされていないビュー:1つ後ろにinsertする
//self.view.insertSubview(notSwipeView, at: 0)
// スワイプされていないビュー:現在の制約無効
if notSwipeView == red{
NSLayoutConstraint.deactivate(conRed)
}
else{
NSLayoutConstraint.deactivate(conGreen)
}
// スワイプされていないビュー:制約有効(拡大)
let conNotSwipe1 = notSwipeView.topAnchor.constraint(equalTo: view.topAnchor)
let conNotSwipe2 = notSwipeView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
let conNotSwipe3 = notSwipeView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
let conNotSwipe4 = notSwipeView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
NSLayoutConstraint.activate([conNotSwipe1, conNotSwipe2, conNotSwipe3, conNotSwipe4])
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01)
{
// スワイプビュー:制約無効(拡大)
NSLayoutConstraint.deactivate(self.saveCon)
// スワイプされていないビュー:制約保持(このビューを縮小するときに使用する)
self.saveCon = [conNotSwipe1, conNotSwipe2, conNotSwipe3, conNotSwipe4]
// 移動処理
var currentConst : [NSLayoutConstraint]?
// 左にスワイプ
if sender.direction == UISwipeGestureRecognizer.Direction.left{
let conMove1 = sender.view!.topAnchor.constraint(equalTo: self.view.topAnchor)
let conMove2 = sender.view!.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
let conMove3 = sender.view!.widthAnchor.constraint(equalToConstant: 768)
let conMove4 = sender.view!.trailingAnchor.constraint(equalTo: self.view.leadingAnchor)
currentConst = [conMove1, conMove2, conMove3, conMove4]
}
// 右にスワイプ
else if sender.direction == UISwipeGestureRecognizer.Direction.right{
let conMove1 = sender.view!.topAnchor.constraint(equalTo: self.view.topAnchor)
let conMove2 = sender.view!.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
let conMove3 = sender.view!.widthAnchor.constraint(equalToConstant: 768)
let conMove4 = sender.view!.leadingAnchor.constraint(equalTo: self.view.trailingAnchor)
currentConst = [conMove1, conMove2, conMove3, conMove4]
}
// スワイプビュー:制約有効(移動)
NSLayoutConstraint.activate(currentConst!)
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
}, completion: { finished in
// スワイプビュー:制約無効
NSLayoutConstraint.deactivate(currentConst!)
// スワイプビュー:元々の制約有効(縮小)
if sender.view! == self.red{
NSLayoutConstraint.activate(self.conRed)
}
else{
NSLayoutConstraint.activate(self.conGreen)
}
// スワイプビュー:後ろに移動する
self.view.insertSubview(sender.view!, at: 0)
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
// 赤ビュー
red.backgroundColor = UIColor.red
red.myDelegate = self
view .addSubview(red)
red.translatesAutoresizingMaskIntoConstraints = false
let constRedA = red.topAnchor.constraint(equalTo: view.topAnchor, constant: 200)
let constRedB = red.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100)
let constRedC = red.widthAnchor.constraint(equalToConstant: 200)
let constRedD = red.heightAnchor.constraint(equalToConstant: 200)
conRed = [constRedA, constRedB, constRedC, constRedD] // 赤ビューの初期制約セット
NSLayoutConstraint.activate(conRed)
// 緑ビュー
green.backgroundColor = UIColor.green
green.myDelegate = self
view .addSubview(green)
green.translatesAutoresizingMaskIntoConstraints = false
let constGreenA = green.topAnchor.constraint(equalTo: view.topAnchor, constant: 200)
let constGreenB = green.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -100)
let constGreenC = green.widthAnchor.constraint(equalToConstant: 200)
let constGreenD = green.heightAnchor.constraint(equalToConstant: 200)
conGreen = [constGreenA, constGreenB, constGreenC, constGreenD] // 緑ビューの初期制約セット
NSLayoutConstraint.activate(conGreen)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
}
// ----------------------------------------------------
// CustomViewDelegate
// ----------------------------------------------------
protocol CustomViewDelegate : class {
func scaleUp(upView: CustomView) // 拡大
func scaleDown(downView: CustomView) // 縮小
func swipeDirection(sender: UISwipeGestureRecognizer) // スワイプ
}
extension CustomViewDelegate
{
func scaleUp(upView: CustomView){}
func scaleDown(downView: CustomView){}
func swipeDirection(sender: UISwipeGestureRecognizer){}
}
// ----------------------------------------------------
// CustomViewクラス
// ----------------------------------------------------
class CustomView: UIView , UIGestureRecognizerDelegate{
weak var myDelegate : CustomViewDelegate?
// MARK: 拡大・縮小
lazy var scaleUpGes : UITapGestureRecognizer = {
let ges: UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(scaleUp(_:)))
ges.numberOfTapsRequired = 1
ges.cancelsTouchesInView = false
ges.delegate = self
return ges
}()
@objc func scaleUp(_ sender: UITapGestureRecognizer){
myDelegate?.scaleUp(upView: self)
scaleUpGes.isEnabled = false
}
// 縮小(とりあえずボタン用意)
lazy var scaleDownBtn : UIButton = {
let btn : UIButton = UIButton(type: .custom)
btn.setTitle("閉じる", for: .normal)
btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20.0)
btn.setTitleColor(UIColor.black, for: .normal)
btn.addTarget(self, action: #selector(scaleDown(_:)), for: .touchUpInside)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
@objc func scaleDown(_ sender: UIButton){
myDelegate?.scaleDown(downView: self)
scaleUpGes.isEnabled = true
}
func setAutoLayout_scaleDownBtn(){
scaleDownBtn.widthAnchor.constraint(equalToConstant: 120).isActive = true
scaleDownBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
scaleDownBtn.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
scaleDownBtn.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 10).isActive = true
}
// MARK: スワイプ
lazy var swipe : [UISwipeGestureRecognizer] = {
var ar : [UISwipeGestureRecognizer] = []
let direction:[UISwipeGestureRecognizer.Direction] = [.left, .right]
direction.forEach{ value in
let sw = UISwipeGestureRecognizer.init(target: self, action: #selector(swipeMove(_:)))
sw.numberOfTouchesRequired = 1
sw.delegate = self
sw.cancelsTouchesInView = false
sw.direction = value
ar.append(sw)
}
return ar
}()
@objc func swipeMove(_ sender: UISwipeGestureRecognizer){
myDelegate?.swipeDirection(sender: sender)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//self.setUp()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setUp()
}
func setUp(){
swipe.forEach{ value in // スワイプジェスチャ
addGestureRecognizer(value)
}
addGestureRecognizer(scaleUpGes) // 拡大ジェスチャ
addSubview(scaleDownBtn) // 縮小ボタン
setAutoLayout_scaleDownBtn()
}
}