This commit is contained in:
DDIsFriend
2023-08-18 17:28:57 +08:00
commit f0e8a1709d
4282 changed files with 192396 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
//
// ButtonsBarView.swift
// SwiftEntryKit_Example
//
// Created by Daniel Huri on 4/28/18.
// Copyright (c) 2018 huri000@gmail.com. All rights reserved.
//
import UIKit
/**
Dynamic button bar view
Buttons are set according to the received content.
1-2 buttons spread horizontally
3 or more buttons spread vertically
*/
final public class EKButtonBarView: UIView {
// MARK: - Properties
private var buttonViews: [EKButtonView] = []
private var separatorViews: [UIView] = []
private let buttonBarContent: EKProperty.ButtonBarContent
private let spreadAxis: QLAxis
private let oppositeAxis: QLAxis
private let relativeEdge: NSLayoutConstraint.Attribute
var bottomCornerRadius: CGFloat = 0 {
didSet {
adjustRoundCornersIfNecessary()
}
}
private lazy var buttonEdgeRatio: CGFloat = {
return 1.0 / CGFloat(self.buttonBarContent.content.count)
}()
private(set) lazy var intrinsicHeight: CGFloat = {
var height: CGFloat = 0
switch buttonBarContent.content.count {
case 0:
height += 1
case 1...buttonBarContent.horizontalDistributionThreshold:
height += buttonBarContent.buttonHeight
default:
for _ in 1...buttonBarContent.content.count {
height += buttonBarContent.buttonHeight
}
}
return height
}()
private var compressedConstraint: NSLayoutConstraint!
private lazy var expandedConstraint: NSLayoutConstraint = {
return set(.height, of: intrinsicHeight, priority: .defaultLow)
}()
// MARK: Setup
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public init(with buttonBarContent: EKProperty.ButtonBarContent) {
self.buttonBarContent = buttonBarContent
if buttonBarContent.content.count <= buttonBarContent.horizontalDistributionThreshold {
spreadAxis = .horizontally
oppositeAxis = .vertically
relativeEdge = .width
} else {
spreadAxis = .vertically
oppositeAxis = .horizontally
relativeEdge = .height
}
super.init(frame: .zero)
setupButtonBarContent()
setupSeparatorViews()
compressedConstraint = set(.height, of: 1, priority: .must)
}
public override func layoutSubviews() {
super.layoutSubviews()
adjustRoundCornersIfNecessary()
}
private func setupButtonBarContent() {
for content in buttonBarContent.content {
let buttonView = EKButtonView(content: content)
addSubview(buttonView)
buttonViews.append(buttonView)
}
layoutButtons()
}
private func layoutButtons() {
guard !buttonViews.isEmpty else {
return
}
let suffix = Array(buttonViews.dropFirst())
if !suffix.isEmpty {
suffix.layout(.height, to: buttonViews.first!)
}
buttonViews.layoutToSuperview(axis: oppositeAxis)
buttonViews.spread(spreadAxis, stretchEdgesToSuperview: true)
buttonViews.layout(relativeEdge, to: self, ratio: buttonEdgeRatio, priority: .must)
}
private func setupTopSeperatorView() {
let topSeparatorView = UIView()
addSubview(topSeparatorView)
topSeparatorView.set(.height, of: 1)
topSeparatorView.layoutToSuperview(.left, .right, .top)
separatorViews.append(topSeparatorView)
}
private func setupSeperatorView(after view: UIView) {
let midSepView = UIView()
addSubview(midSepView)
let sepAttribute: NSLayoutConstraint.Attribute
let buttonAttribute: NSLayoutConstraint.Attribute
switch oppositeAxis {
case .vertically:
sepAttribute = .centerX
buttonAttribute = .right
case .horizontally:
sepAttribute = .centerY
buttonAttribute = .bottom
}
midSepView.layout(sepAttribute, to: buttonAttribute, of: view)
midSepView.set(relativeEdge, of: 1)
midSepView.layoutToSuperview(axis: oppositeAxis)
separatorViews.append(midSepView)
}
private func setupSeparatorViews() {
setupTopSeperatorView()
for button in buttonViews.dropLast() {
setupSeperatorView(after: button)
}
setupInterfaceStyle()
}
// Amination
public func expand() {
let expansion = {
self.compressedConstraint.priority = .defaultLow
self.expandedConstraint.priority = .must
/* NOTE: Calling layoutIfNeeded for the whole view hierarchy.
Sometimes it's easier to just use frames instead of AutoLayout for
hierarch complexity considerations. Here the animation influences almost the
entire view hierarchy. */
SwiftEntryKit.layoutIfNeeded()
}
alpha = 1
if buttonBarContent.expandAnimatedly {
let damping: CGFloat = buttonBarContent.content.count <= 2 ? 0.4 : 0.8
SwiftEntryKit.layoutIfNeeded()
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: damping, initialSpringVelocity: 0, options: [.beginFromCurrentState, .allowUserInteraction, .layoutSubviews, .allowAnimatedContent], animations: {
expansion()
}, completion: nil)
} else {
expansion()
}
}
public func compress() {
compressedConstraint.priority = .must
expandedConstraint.priority = .defaultLow
}
private func adjustRoundCornersIfNecessary() {
let size = CGSize(width: bottomCornerRadius, height: bottomCornerRadius)
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: .bottom, cornerRadii: size)
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
layer.mask = maskLayer
}
private func setupInterfaceStyle() {
for view in separatorViews {
view.backgroundColor = buttonBarContent.separatorColor(for: traitCollection)
}
}
public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
setupInterfaceStyle()
}
}

View File

@@ -0,0 +1,97 @@
//
// EKButtonView.swift
// SwiftEntryKit
//
// Created by Daniel Huri on 12/8/18.
// Copyright © 2018 CocoaPods. All rights reserved.
//
import UIKit
final class EKButtonView: UIView {
// MARK: - Properties
private let button = UIButton()
private let titleLabel = UILabel()
private let content: EKProperty.ButtonContent
// MARK: - Setup
init(content: EKProperty.ButtonContent) {
self.content = content
super.init(frame: .zero)
setupTitleLabel()
setupButton()
setupAcceessibility()
setupInterfaceStyle()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupAcceessibility() {
isAccessibilityElement = false
button.isAccessibilityElement = true
button.accessibilityIdentifier = content.accessibilityIdentifier
button.accessibilityLabel = content.label.text
}
private func setupButton() {
addSubview(button)
button.fillSuperview()
button.addTarget(self, action: #selector(buttonTouchUp),
for: [.touchUpInside, .touchUpOutside, .touchCancel])
button.addTarget(self, action: #selector(buttonTouchDown),
for: .touchDown)
button.addTarget(self, action: #selector(buttonTouchUpInside),
for: .touchUpInside)
}
private func setupTitleLabel() {
titleLabel.numberOfLines = content.label.style.numberOfLines
titleLabel.font = content.label.style.font
titleLabel.text = content.label.text
titleLabel.textAlignment = .center
titleLabel.lineBreakMode = .byWordWrapping
addSubview(titleLabel)
titleLabel.layoutToSuperview(axis: .horizontally,
offset: content.contentEdgeInset)
titleLabel.layoutToSuperview(axis: .vertically,
offset: content.contentEdgeInset)
}
private func setBackground(by content: EKProperty.ButtonContent,
isHighlighted: Bool) {
if isHighlighted {
backgroundColor = content.highlightedBackgroundColor(for: traitCollection)
} else {
backgroundColor = content.backgroundColor(for: traitCollection)
}
}
private func setupInterfaceStyle() {
backgroundColor = content.backgroundColor(for: traitCollection)
titleLabel.textColor = content.label.style.color(for: traitCollection)
}
// MARK: - Selectors
@objc func buttonTouchUpInside() {
content.action?()
}
@objc func buttonTouchDown() {
setBackground(by: content, isHighlighted: true)
}
@objc func buttonTouchUp() {
setBackground(by: content, isHighlighted: false)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
setupInterfaceStyle()
}
}

View File

@@ -0,0 +1,69 @@
//
// EKRatingSymbolView.swift
// SwiftEntryKit
//
// Created by Daniel Huri on 6/1/18.
// Copyright (c) 2018 huri000@gmail.com. All rights reserved.
//
import UIKit
final public class EKRatingSymbolView: UIView {
private let button = UIButton()
private let imageView = UIImageView()
private let unselectedImage: EKProperty.ImageContent
private let selectedImage: EKProperty.ImageContent
var selection: EKRatingMessage.Selection
public var isSelected: Bool {
set {
imageView.imageContent = newValue ? selectedImage : unselectedImage
}
get {
return imageView.image == selectedImage.images.first
}
}
public init(unselectedImage: EKProperty.ImageContent, selectedImage: EKProperty.ImageContent, selection: @escaping EKRatingMessage.Selection) {
self.unselectedImage = unselectedImage
self.selectedImage = selectedImage
self.selection = selection
super.init(frame: .zero)
setupImageView()
setupButton()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupButton() {
addSubview(button)
button.fillSuperview()
button.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
button.addTarget(self, action: #selector(touchDown), for: [.touchDown])
button.addTarget(self, action: #selector(touchUp), for: [.touchUpInside, .touchUpOutside, .touchCancel])
}
private func setupImageView() {
addSubview(imageView)
imageView.imageContent = unselectedImage
imageView.centerInSuperview()
imageView.sizeToSuperview(withRatio: 0.7)
}
@objc func touchUpInside() {
selection(tag)
}
@objc func touchDown() {
transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
}
@objc func touchUp() {
transform = CGAffineTransform(scaleX: 1, y: 1)
}
}

View File

@@ -0,0 +1,62 @@
//
// EKRatingSymbolsContainerView.swift
// SwiftEntryKit
//
// Created by Daniel Huri on 6/1/18.
// Copyright (c) 2018 huri000@gmail.com. All rights reserved.
//
import UIKit
final public class EKRatingSymbolsContainerView: UIView {
private var message: EKRatingMessage!
private var symbolsArray: [EKRatingSymbolView] = []
public func setup(with message: EKRatingMessage,
externalSelection: @escaping EKRatingMessage.Selection) {
self.message = message
let internalSelection = { [unowned self] (index: Int) in
self.select(index: index)
externalSelection(index)
}
for (index, item) in message.ratingItems.enumerated() {
let itemView = EKRatingSymbolView(unselectedImage: item.unselectedImage,
selectedImage: item.selectedImage,
selection: internalSelection)
itemView.tag = index
addSubview(itemView)
itemView.set(.height, of: item.size.height)
itemView.set(.width, of: item.size.width)
symbolsArray.append(itemView)
}
symbolsArray.layoutToSuperview(axis: .vertically, priority: .must)
symbolsArray.spread(.horizontally, stretchEdgesToSuperview: true)
select(index: message.selectedIndex)
}
private func select(index: Int? = nil) {
var delay: TimeInterval = 0
for (i, view) in symbolsArray.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
if let index = index, i <= index {
view.isSelected = true
view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
} else if view.isSelected || index == nil {
view.isSelected = false
view.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
}
UIView.animate(withDuration: 0.6,
delay: 0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0,
options: [.allowUserInteraction], animations: {
view.transform = .identity
}, completion: nil)
}
delay += 0.05
}
}
}

View File

@@ -0,0 +1,88 @@
//
// EKTextField.swift
// SwiftEntryKit
//
// Created by Daniel Huri on 5/16/18.
//
import Foundation
import UIKit
final public class EKTextField: UIView {
// MARK: - Properties
static let totalHeight: CGFloat = 45
private let content: EKProperty.TextFieldContent
private let imageView = UIImageView()
private let textField = UITextField()
private let separatorView = UIView()
public var text: String {
set {
textField.text = newValue
}
get {
return textField.text ?? ""
}
}
// MARK: - Setup
public init(with content: EKProperty.TextFieldContent) {
self.content = content
super.init(frame: UIScreen.main.bounds)
setupImageView()
setupTextField()
setupSeparatorView()
textField.accessibilityIdentifier = content.accessibilityIdentifier
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupImageView() {
addSubview(imageView)
imageView.contentMode = .center
imageView.set(.width, .height, of: EKTextField.totalHeight)
imageView.layoutToSuperview(.leading)
imageView.image = content.leadingImage
imageView.tintColor = content.tintColor(for: traitCollection)
}
private func setupTextField() {
addSubview(textField)
textField.textFieldContent = content
textField.delegate = content.delegate
textField.set(.height, of: EKTextField.totalHeight)
textField.layout(.leading, to: .trailing, of: imageView)
textField.layoutToSuperview(.top, .trailing)
imageView.layout(to: .centerY, of: textField)
}
private func setupSeparatorView() {
addSubview(separatorView)
separatorView.layout(.top, to: .bottom, of: textField)
separatorView.set(.height, of: 1)
separatorView.layoutToSuperview(.bottom)
separatorView.layoutToSuperview(axis: .horizontally, offset: 10)
separatorView.backgroundColor = content.bottomBorderColor.color(
for: traitCollection,
mode: content.displayMode
)
}
public func makeFirstResponder() {
textField.becomeFirstResponder()
}
public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
separatorView.backgroundColor = content.bottomBorderColor(for: traitCollection)
imageView.tintColor = content.tintColor(for: traitCollection)
textField.textColor = content.textStyle.color(for: traitCollection)
textField.placeholder = content.placeholder
}
}

View File

@@ -0,0 +1,18 @@
//
// EntryAppearanceDescriptor.swift
// SwiftEntryKit
//
// Created by Daniel Huri on 1/5/19.
// Copyright © 2019 CocoaPods. All rights reserved.
//
import UIKit
/**
An anti-pattern for SwiftEntryKit views to know more about their appearence,
if necessary, since views don't have access to EKAttributes.
This is a solution to bug #117 (round buttons in alert)
*/
protocol EntryAppearanceDescriptor: AnyObject {
var bottomCornerRadius: CGFloat { get set }
}