|
@@ -0,0 +1,334 @@
|
|
|
+//
|
|
|
+// NCCustomizableAlertController.swift
|
|
|
+// Nextcloud
|
|
|
+//
|
|
|
+// Created by Marino Faggiana on 30/09/19.
|
|
|
+// Copyright © 2018 Marino Faggiana. All rights reserved.
|
|
|
+//
|
|
|
+// Author Marino Faggiana <marino.faggiana@nextcloud.com>
|
|
|
+//
|
|
|
+// This program is free software: you can redistribute it and/or modify
|
|
|
+// it under the terms of the GNU General Public License as published by
|
|
|
+// the Free Software Foundation, either version 3 of the License, or
|
|
|
+// (at your option) any later version.
|
|
|
+//
|
|
|
+// This program is distributed in the hope that it will be useful,
|
|
|
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+// GNU General Public License for more details.
|
|
|
+//
|
|
|
+// You should have received a copy of the GNU General Public License
|
|
|
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+//
|
|
|
+
|
|
|
+// Medium article
|
|
|
+//
|
|
|
+// https://medium.com/@Daniel_illescas/hacking-ios-alerts-with-swift-61aefce9736a
|
|
|
+
|
|
|
+import UIKit
|
|
|
+
|
|
|
+final class DarkAlertController: NCCustomizableAlertController {
|
|
|
+
|
|
|
+ override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+
|
|
|
+ self.visualEffectView?.effect = UIBlurEffect(style: .dark)
|
|
|
+ self.tintColor = UIColor(red: 0.4, green: 0.5, blue: 1.0, alpha: 1.0)
|
|
|
+ self.contentView?.backgroundColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 0.7)
|
|
|
+
|
|
|
+ let whiteStringAttribute = StringAttribute(key: .foregroundColor, value: UIColor.white)
|
|
|
+ self.titleAttributes = [whiteStringAttribute]
|
|
|
+ self.messageAttributes = [whiteStringAttribute]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+
|
|
|
+open class NCCustomizableAlertController: UIAlertController {
|
|
|
+
|
|
|
+ open lazy var visualEffectView: UIVisualEffectView? = {
|
|
|
+ return self.view.visualEffectView
|
|
|
+ }()
|
|
|
+
|
|
|
+ open lazy var lazyContentView: UIView? = {
|
|
|
+ return self.contentView
|
|
|
+ }()
|
|
|
+
|
|
|
+ open lazy var tintColor: UIColor? = {
|
|
|
+ return self.view.tintColor
|
|
|
+ }()
|
|
|
+
|
|
|
+ override open func viewWillAppear(_ animated: Bool) {
|
|
|
+ super.viewDidAppear(animated)
|
|
|
+ self.view.tintColor = self.tintColor
|
|
|
+ }
|
|
|
+
|
|
|
+ func addParallaxEffect(x: Int = 20, y: Int = 20) {
|
|
|
+ let horizontal = UIInterpolatingMotionEffect(keyPath: "layer.transform.translation.x", type: .tiltAlongHorizontalAxis)
|
|
|
+ horizontal.minimumRelativeValue = -x
|
|
|
+ horizontal.maximumRelativeValue = x
|
|
|
+
|
|
|
+ let vertical = UIInterpolatingMotionEffect(keyPath: "layer.transform.translation.y", type: .tiltAlongVerticalAxis)
|
|
|
+ vertical.minimumRelativeValue = -y
|
|
|
+ vertical.maximumRelativeValue = y
|
|
|
+
|
|
|
+ let motionEffectsGroup = UIMotionEffectGroup()
|
|
|
+ motionEffectsGroup.motionEffects = [horizontal, vertical]
|
|
|
+
|
|
|
+ self.view.addMotionEffect(motionEffectsGroup)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+
|
|
|
+extension UIAlertController {
|
|
|
+
|
|
|
+ private var visualEffectView: UIVisualEffectView? {
|
|
|
+ return self.view.visualEffectView
|
|
|
+ }
|
|
|
+
|
|
|
+ var contentView: UIView? {
|
|
|
+ return self.view.subviews.first?.subviews.first?.subviews.first
|
|
|
+ }
|
|
|
+
|
|
|
+ var titleAttributes: [StringAttribute] {
|
|
|
+ get { return self.attributedTitle_?.attributes ?? [] }
|
|
|
+ set { self.attributedTitle_ = newValue.suitableAttributedText(forText: self.title) }
|
|
|
+ }
|
|
|
+
|
|
|
+ var messageAttributes: [StringAttribute] {
|
|
|
+ get { return self.attributedMessage_?.attributes ?? [] }
|
|
|
+ set { self.attributedMessage_ = newValue.suitableAttributedText(forText: self.message) }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension UIAlertAction {
|
|
|
+
|
|
|
+ var label: UILabel? {
|
|
|
+ return (self.value(forKey: "__representer") as? NSObject)?.value(forKey: "label") as? UILabel
|
|
|
+ }
|
|
|
+
|
|
|
+ var titleAttributes: [StringAttribute] {
|
|
|
+ get { return self.label?.attributedText?.attributes ?? [] }
|
|
|
+ set { self.label?.textAttributes = newValue }
|
|
|
+ }
|
|
|
+
|
|
|
+ var titleTextColor: UIColor? {
|
|
|
+ get { return self.titleTextColor_ }
|
|
|
+ set { self.titleTextColor_ = newValue }
|
|
|
+ }
|
|
|
+
|
|
|
+ var accessoryImage: UIImage? {
|
|
|
+ get { return self.image_ }
|
|
|
+ set { self.image_ = newValue }
|
|
|
+ }
|
|
|
+
|
|
|
+ var contentElementViewController: ElementViewController? {
|
|
|
+ get { return self.contentViewController_ as? ElementViewController }
|
|
|
+ set {
|
|
|
+ if accessoryImage != nil {
|
|
|
+ print("The accessory image might overlap with the content of the contentViewController")
|
|
|
+ }
|
|
|
+ self.contentViewController_ = newValue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var contentViewController: UIViewController? {
|
|
|
+ get { return self.contentViewController_ }
|
|
|
+ set {
|
|
|
+ if accessoryImage != nil {
|
|
|
+ print("The accessory image might overlap with the content of the contentViewController")
|
|
|
+ }
|
|
|
+ self.contentViewController_ = newValue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var tableViewController: UITableViewController? {
|
|
|
+ get { return self.contentViewController_ as? UITableViewController }
|
|
|
+ set {
|
|
|
+ if accessoryImage != nil {
|
|
|
+ print("The accessory image might overlap with the content of the contentViewController")
|
|
|
+ }
|
|
|
+ self.contentViewController_ = newValue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var accessoryView: UIView? {
|
|
|
+ get { return contentElementViewController?.elementView }
|
|
|
+ set {
|
|
|
+ let elementViewController = ElementViewController()
|
|
|
+ elementViewController.elementView = newValue
|
|
|
+ self.contentViewController = elementViewController
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension UILabel {
|
|
|
+ var textAttributes: [StringAttribute] {
|
|
|
+ get { return self.attributedText?.attributes ?? [] }
|
|
|
+ set { self.attributedText = newValue.suitableAttributedText(forText: self.text) }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension NSAttributedString {
|
|
|
+
|
|
|
+ struct StringAttribute {
|
|
|
+
|
|
|
+ let key: NSAttributedString.Key
|
|
|
+ let value: Any
|
|
|
+ var range: NSRange? = nil
|
|
|
+
|
|
|
+ init(key: NSAttributedString.Key, value: Any, range: NSRange? = nil) {
|
|
|
+ self.key = key
|
|
|
+ self.value = value
|
|
|
+ self.range = range
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var attributes: [StringAttribute] {
|
|
|
+
|
|
|
+ var savedAttributes: [StringAttribute] = []
|
|
|
+
|
|
|
+ let rawAttributes = self.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: self.length))
|
|
|
+
|
|
|
+ for rawAttribute in rawAttributes {
|
|
|
+ savedAttributes.append(StringAttribute(key: rawAttribute.key, value: rawAttribute.value))
|
|
|
+ }
|
|
|
+ return savedAttributes
|
|
|
+ }
|
|
|
+ convenience init(string: String?, attributes: [StringAttribute]) {
|
|
|
+
|
|
|
+ guard let validString = string else { self.init(string: "", attributes: [:]); return }
|
|
|
+
|
|
|
+ var attributesDict: [NSAttributedString.Key: Any] = [:]
|
|
|
+ for attribute in attributes {
|
|
|
+ attributesDict[attribute.key] = attribute.value
|
|
|
+ }
|
|
|
+ self.init(string: validString, attributes: attributesDict)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension NSMutableAttributedString {
|
|
|
+ var mutableAttributes: [StringAttribute] {
|
|
|
+ get { return self.attributes }
|
|
|
+ set {
|
|
|
+ let defaultRange = NSRange(location: 0, length: self.length)
|
|
|
+ for attribute in newValue {
|
|
|
+ self.addAttribute(attribute.key, value: attribute.value, range: attribute.range ?? defaultRange)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ convenience init(string: String?, mutableAttributes: [StringAttribute]) {
|
|
|
+ guard let text = string else { self.init(string: ""); return }
|
|
|
+ self.init(string: text)
|
|
|
+ self.mutableAttributes = mutableAttributes
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class ElementViewController: UIViewController {
|
|
|
+
|
|
|
+ var elementView: UIView? = nil
|
|
|
+
|
|
|
+ override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+
|
|
|
+ if let validView = self.elementView {
|
|
|
+
|
|
|
+ self.view.addSubview(validView)
|
|
|
+
|
|
|
+ validView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+
|
|
|
+ let margins = self.view.layoutMarginsGuide
|
|
|
+
|
|
|
+ validView.centerYAnchor.constraint(equalTo: margins.topAnchor, constant: validView.frame.height).isActive = true
|
|
|
+ validView.centerXAnchor.constraint(equalTo: margins.centerXAnchor).isActive = true
|
|
|
+ validView.widthAnchor.constraint(equalTo: margins.widthAnchor).isActive = true
|
|
|
+ validView.heightAnchor.constraint(equalTo: margins.heightAnchor).isActive = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//
|
|
|
+
|
|
|
+typealias StringAttribute = NSAttributedString.StringAttribute
|
|
|
+
|
|
|
+extension Array where Element == StringAttribute {
|
|
|
+ func suitableAttributedText(forText text: String?) -> NSAttributedString {
|
|
|
+ if self.compactMap({ $0.range }).isEmpty {
|
|
|
+ return NSAttributedString(string: text, attributes: self)
|
|
|
+ }
|
|
|
+ return NSMutableAttributedString(string: text, mutableAttributes: self)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension UIView {
|
|
|
+ func mapEverySubview(predicate: (UIView) -> Void) {
|
|
|
+ predicate(self)
|
|
|
+ for subview in self.subviews {
|
|
|
+ subview.mapEverySubview(predicate: predicate)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+private extension UIView {
|
|
|
+ var visualEffectView: UIVisualEffectView? {
|
|
|
+
|
|
|
+ if self is UIVisualEffectView {
|
|
|
+ return self as? UIVisualEffectView
|
|
|
+ }
|
|
|
+
|
|
|
+ for subview in self.subviews {
|
|
|
+ if let validView = subview.visualEffectView {
|
|
|
+ return validView
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private extension UIAlertController {
|
|
|
+
|
|
|
+ private var attributedTitle_: NSAttributedString? {
|
|
|
+ get {
|
|
|
+ return self.value(forKey: "attributedTitle") as? NSAttributedString
|
|
|
+ } set {
|
|
|
+ self.setValue(newValue, forKey: "attributedTitle")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private var attributedMessage_: NSAttributedString? {
|
|
|
+ get {
|
|
|
+ return self.value(forKey: "attributedMessage") as? NSAttributedString
|
|
|
+ } set {
|
|
|
+ self.setValue(newValue, forKey: "attributedMessage")
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+private extension UIAlertAction {
|
|
|
+
|
|
|
+ // idea from: https://medium.com/@maximbilan/ios-uialertcontroller-customization-5cfd88140db8
|
|
|
+ private var image_: UIImage? {
|
|
|
+ get {
|
|
|
+ return self.value(forKey: "image") as? UIImage
|
|
|
+ } set {
|
|
|
+// let imageWithGoodDimensions = newValue?.scale(to: CGSize(width: 30, height: 30))
|
|
|
+// self.setValue(imageWithGoodDimensions?.withRenderingMode(.alwaysOriginal), forKey: "image")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private var contentViewController_: UIViewController? {
|
|
|
+ get {
|
|
|
+ return self.value(forKey: "contentViewController") as? UIViewController
|
|
|
+ } set {
|
|
|
+ self.setValue(newValue, forKey: "contentViewController")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private var titleTextColor_: UIColor? {
|
|
|
+ get {
|
|
|
+ return self.value(forKey: "titleTextColor") as? UIColor
|
|
|
+ } set {
|
|
|
+ self.setValue(newValue, forKey: "titleTextColor")
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|