|
//
// AlertView.swift
// PaiaiUIKit
//
// Created by FFIB on 2017/11/14.
// Copyright © 2017年 FFIB. All rights reserved.
//
import UIKit
public final class AlertView: UIView {
public static var `default`: AlertView {
return AlertView()
}
private typealias ItemAction = ((AlertItem) -> Void)
private var confirmAction: AlertAction?
private var cancelAction: AlertAction?
public var cancelItem: AlertItem = {
let item = AlertItem(type: .custom)
item.backgroundColor = UIColor(r: 214, g: 214, b: 214)
item.setTitleColor(UIColor(r: 51, g: 51, b: 51), for: .normal)
return item
}()
public var confirmItem: AlertItem = {
let item = AlertItem(type: .custom)
item.backgroundColor = UIColor(r: 129, g: 209, b: 53)
item.setTitleColor(UIColor.white, for: .normal)
return item
}()
public var titleLabel: UILabel = {
var label = UILabel()
label.numberOfLines = 0
label.textAlignment = .left
label.backgroundColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 17)
label.textColor = UIColor(r: 53, g: 53, b: 53)
return label
}()
public var messageLabel: UILabel = {
var label = UILabel()
label.numberOfLines = 0
label.textAlignment = .left
label.backgroundColor = UIColor.white
label.font = UIFont.systemFont(ofSize: 15)
label.textColor = UIColor(r: 153, g: 153, b: 153)
return label
}()
public var contentView: UIView?
private var viewNotReady = true
private var bottomView: UIView?
private var topView: UIView?
var title: String = "" {
didSet {
titleLabel.text = title
}
}
var message: String = "" {
didSet {
messageLabel.text = message
}
}
override public func didMoveToWindow() {
super.didMoveToWindow()
guard viewNotReady else { return }
constructViewHierarchy()
activateConstraints()
installTarget()
backgroundColor = UIColor.white
viewNotReady = false
}
private func constructViewHierarchy() {
if !title.isEmpty { addSubview(titleLabel) }
if !message.isEmpty { addSubview(messageLabel) }
if cancelAction != nil { addSubview(cancelItem) }
if confirmAction != nil { addSubview(confirmItem) }
if let contentView = contentView { addSubview(contentView) }
}
private func activateConstraints() {
activateConstraintsItems()
activateConstraintsLabels()
activateConstraintsContentView()
activateConstraintsRootView()
}
func addAlertAction(_ action: AlertAction) {
switch action.style {
case .default:
confirmAction = action
case .cancel:
cancelAction = action
case let .custom(item):
confirmAction = action
confirmItem = item
}
}
private func installTarget() {
if cancelAction != nil {
cancelItem.addTarget(self, action: #selector(cancelAction(btn:)), for: .touchUpInside)
}
if confirmAction != nil {
confirmItem.addTarget(self, action: #selector(confirmAction(btn:)), for: .touchUpInside)
}
}
@objc private func confirmAction(btn: UIButton) {
confirmAction?.handler?(confirmItem)
dismissSuperViewController()
}
@objc private func cancelAction(btn: UIButton) {
cancelAction?.handler?(cancelItem)
dismissSuperViewController()
}
private func dismissSuperViewController() {
guard let vc = getSuperViewController() else { return }
vc.dismissController()
}
}
/// MARK: layout
fileprivate extension AlertView {
func activateConstraintsRootView() {
guard let v = superview else { return }
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
centerYAnchor.constraint(equalTo: v.centerYAnchor),
centerXAnchor.constraint(equalTo: v.centerXAnchor),
leadingAnchor.constraint(equalTo: v.leadingAnchor, constant: 40),
trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: -40),
])
if #available(iOS 11, *) {
directionalLayoutMargins = NSDirectionalEdgeInsets(top: 10, leading: 16, bottom: 0, trailing: 16)
} else {
layoutMargins = UIEdgeInsets(top: 10, left: 16, bottom: 0, right: 16)
}
guard let topView = topView else { return }
NSLayoutConstraint.activate([
topView.bottomAnchor.constraint(equalTo: bottomView?.topAnchor ?? bottomAnchor, constant: -12)
])
}
func activateConstraintsContentView() {
guard let contentView = contentView else { return }
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.topAnchor.constraint(equalTo: topView?.bottomAnchor ?? topAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomView?.bottomAnchor ?? bottomAnchor)
])
bottomView = contentView
}
func activateConstraintsItems() {
let (alertActions, items) = getActionsAndItems()
guard !alertActions.isEmpty, let width = superview?.bounds.width else { return }
/// center view spacing is 80
/// margins 32
let spacing: CGFloat = 80 + 32
let itemSpacing: CGFloat = CGFloat((items.count - 1) * 6)
let w = (width - spacing - itemSpacing) / CGFloat(items.count)
var last: AlertItem? = nil
var leading: CGFloat = 0
for (action, item) in zip(alertActions, items) {
item.translatesAutoresizingMaskIntoConstraints = false
item.setTitle(action.title, for: .normal)
NSLayoutConstraint.activate([
item.widthAnchor.constraint(equalToConstant: w),
item.heightAnchor.constraint(equalToConstant: 36),
item.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -14),
item.leadingAnchor.constraint(equalTo: last?.trailingAnchor ?? layoutMarginsGuide.leadingAnchor, constant: leading)
])
leading = 6
last = item
}
bottomView = last
}
func activateConstraintsLabels() {
var labels: [UILabel] = []
if !title.isEmpty { labels.append(titleLabel) }
if !message.isEmpty { labels.append(messageLabel) }
if labels.isEmpty { return }
var last: UILabel? = nil
for label in labels {
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
label.topAnchor.constraint(equalTo: last?.bottomAnchor ?? layoutMarginsGuide.topAnchor, constant: 12),
])
last = label
}
topView = last
}
}
fileprivate extension AlertView {
func getActionsAndItems() -> ([AlertAction], [AlertItem]) {
let alertActions = [cancelAction, confirmAction].compactMap { $0 }
let items = alertActions.map { (action) -> AlertItem in
switch action.style {
case .cancel:
return cancelItem
case .default, .custom:
return confirmItem
}
}
return (alertActions, items)
}
}
|