Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sharing selected target view and slide in / out animation #31

Merged
merged 7 commits into from
Mar 29, 2019
Merged
4 changes: 4 additions & 0 deletions LineSDK/LineSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@
DBF20278212C137D00780358 /* GetApproversInGroupRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF20277212C137D00780358 /* GetApproversInGroupRequest.swift */; };
DBF2027B212D58FC00780358 /* GetGroupsRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF2027A212D58FC00780358 /* GetGroupsRequestTests.swift */; };
DBF2027D212D5B5200780358 /* GetApproversInGroupRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBF2027C212D5B5200780358 /* GetApproversInGroupRequestTests.swift */; };
DBFBD439224B85F500A74D44 /* SelectedTargetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFBD438224B85F500A74D44 /* SelectedTargetView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -532,6 +533,7 @@
DBF20277212C137D00780358 /* GetApproversInGroupRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetApproversInGroupRequest.swift; sourceTree = "<group>"; };
DBF2027A212D58FC00780358 /* GetGroupsRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetGroupsRequestTests.swift; sourceTree = "<group>"; };
DBF2027C212D5B5200780358 /* GetApproversInGroupRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetApproversInGroupRequestTests.swift; sourceTree = "<group>"; };
DBFBD438224B85F500A74D44 /* SelectedTargetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedTargetView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -696,6 +698,7 @@
4B5B0EF22241DEA900BA59A0 /* ShareViewController.swift */,
4B5B0EF42241DF3E00BA59A0 /* PageViewController.swift */,
4B3D78BA22420BEA00DE27D1 /* PageTabView.swift */,
DBFBD438224B85F500A74D44 /* SelectedTargetView.swift */,
);
path = SharingUI;
sourceTree = "<group>";
Expand Down Expand Up @@ -1638,6 +1641,7 @@
4BFD605C212A8775009E9838 /* FlexTextComponent.swift in Sources */,
4B9A305421215A1A00174C6F /* TemplateConfirmPayload.swift in Sources */,
4BF45A8221377DCF00CCD28E /* CryptoError.swift in Sources */,
DBFBD439224B85F500A74D44 /* SelectedTargetView.swift in Sources */,
4BFD6062212A8F55009E9838 /* FlexMessageComponent.swift in Sources */,
4B4464E8212D093D008D3624 /* TemplateMessageProperties.swift in Sources */,
4B45256B2101810D00A39D4F /* LoginProcess.swift in Sources */,
Expand Down
109 changes: 109 additions & 0 deletions LineSDK/LineSDK/SharingUI/SelectedTargetView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// SelectedTargetView.swift
//
// Copyright (c) 2016-present, LINE Corporation. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by LINE Corporation.
//
// As with any software that integrates with the LINE Corporation platform, your use of this software
// is subject to the LINE Developers Agreement [http://terms2.line.me/LINE_Developers_Agreement].
// This copyright notice shall be included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import UIKit

class SelectedTargetView: UIView {
enum Design {
static var height: CGFloat { return 79 }
static var bgColor: UIColor { return .init(hex6: 0xF7F8FA) }
static var borderColor: UIColor { return .init(hex6: 0xE6E7EA) }
static var borderWidth: CGFloat { return 0.5 }
}

private var slideAnimationViewTopConstraint: NSLayoutConstraint!

private let slideAnimationView: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = Design.bgColor
view.layer.borderWidth = Design.borderWidth
view.layer.borderColor = Design.borderColor.cgColor
return view
}()

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear

setupSubviews()

setupLayouts()
}

private func setupSubviews() {
addSubview(slideAnimationView)
}

private func setupLayouts() {
slideAnimationView.translatesAutoresizingMaskIntoConstraints = false
slideAnimationViewTopConstraint = slideAnimationView.topAnchor.constraint(equalTo: topAnchor)
slideAnimationViewTopConstraint.isActive = true
NSLayoutConstraint.activate([
slideAnimationView.leadingAnchor.constraint(equalTo: leadingAnchor),
slideAnimationView.trailingAnchor.constraint(equalTo: trailingAnchor),
slideAnimationView.heightAnchor.constraint(equalTo: heightAnchor)
])
}

enum Mode {
case show
case hide
}

func setMode(_ mode: Mode, animated: Bool) {
self.mode = mode
updateLayout(animated: animated)
}

private(set) var mode = Mode.hide

private func updateLayout(animated: Bool) {
self.slideAnimationViewTopConstraint.isActive = false
let anchor: NSLayoutYAxisAnchor
let alpha: CGFloat
switch self.mode {
case .show:
anchor = topAnchor
isUserInteractionEnabled = true
alpha = 1
case .hide:
anchor = bottomAnchor
isUserInteractionEnabled = false
alpha = 0
}
self.slideAnimationViewTopConstraint = slideAnimationView.topAnchor.constraint(equalTo: anchor)
self.slideAnimationViewTopConstraint.isActive = true

if animated {
UIView.animate(withDuration: 0.2) {
self.alpha = alpha
self.layoutIfNeeded()
}
} else {
self.alpha = alpha
self.layoutIfNeeded()
}
Copy link
Member

@onevcat onevcat Mar 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess if animated == false, the alpha is not getting updated now. Maybe we also need the case for non-animated?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I suggest also call layoutIfNeeded() in the animated == false statement, to trigger an immediate layout. Otherwise, any code depends on the layout would not work until the next main run loop, which causes confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I missed the alpha setup in no animation case.
Agree. the layoutIfNeeded in animated == false will be more clear.

}
}
45 changes: 45 additions & 0 deletions LineSDK/LineSDK/SharingUI/ShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import UIKit

public class ShareViewController: UINavigationController {

private lazy var selectedTargetView = SelectedTargetView()

public init() {
let v1 = UIViewController()
v1.view.backgroundColor = .red
Expand All @@ -45,11 +47,54 @@ public class ShareViewController: UINavigationController {
super.init(rootViewController: root)
}

@objc
func foo() {
selectedTargetView.setMode((selectedTargetView.mode == .show) ? .hide : .show,
animated: true)
}

public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Lift Cycle
public override func viewDidLoad() {
super.viewDidLoad()

setupSubviews()

setupLayouts()

// default
selectedTargetView.setMode(.hide, animated: false)

// TODO: Remove this mocked entry
let btn = UIButton(type: .system)
btn.setTitleColor(.black, for: .normal)
btn.setTitle("toggle SelectedTargetView", for: .normal)
btn.addTarget(self, action: #selector(foo), for: .touchUpInside)
btn.sizeToFit()
btn.center = view.center
self.view.addSubview(btn)
//
}

private func setupSubviews() {
view.addSubview(selectedTargetView)
}

private func setupLayouts() {
selectedTargetView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
selectedTargetView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
selectedTargetView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
selectedTargetView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
selectedTargetView.topAnchor.constraint(equalTo: safeBottomAnchor,
constant: -SelectedTargetView.Design.height)
])
}
}