fix: code review notes + added custom modifier for alerts

This commit is contained in:
Nikita Semenov 2022-07-27 13:58:44 +03:00
parent cfbf53faf8
commit 9a427adab7
11 changed files with 111 additions and 32 deletions

View File

@ -3,16 +3,26 @@
Core UI elements: protocols, views and helpers.
## SwiftUI alerts
> SwiftUI views should conform to protocol `SwiftUIAlertContext` to present alert
> SwiftUI views should conform to protocol `SwiftUIAlertContext` to present alerts. This means that the view must implement the presentingViewController property. This controller is a context from which the alert will be shown.
```swift
var body: some View {
Button("Show custom alert with binding property") {
alertDescription = factory.okAlert(title: "Title", message: "Message")
isPresentedCustomAlert = true
struct ContentView: View, SwiftUIAlertContext {
private let factory = AlertFactory()
@State private var alertDescription: AlertDescriptor
@State private var isAlertPresented = false
var presentingViewController: UIViewController
var body: some View {
Button("Show custom alert with binding property") {
alertDescription = factory.okAlert(title: "Title", message: "Message")
isAlertPresented = true
}
}
.alert(isPresented: $isAlertPresented, on: self, alert: alertDescription)
}
.alert(isPresented: $isPresentedAlert, on: self, alert: alertDescription)
}
```

View File

@ -0,0 +1,51 @@
//
// Copyright (c) 2022 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission 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 SwiftUI
import TISwiftUtils
import TIUIKitCore
@available(iOS 13, *)
struct AlertModifier: ViewModifier {
@Binding var isPresented: Bool
let context: AlertPresentationContext
let alertDescriptor: AlertDescriptor
let alertPresentable: AlertPresentable?
func body(content: Content) -> some View {
if isPresented {
let completion: VoidClosure = {
isPresented = false
}
if let alertPresentable = alertPresentable {
alertPresentable.present(on: context, completion: completion)
} else {
alertDescriptor.present(on: context, completion: completion)
}
}
return content
}
}

View File

@ -36,13 +36,10 @@ public extension View {
on context: AlertPresentationContext,
alert: AlertDescriptor) -> some View {
if isPresented.wrappedValue {
alert.present(on: context) {
isPresented.wrappedValue = false
}
}
return self
modifier(AlertModifier(isPresented: isPresented,
context: context,
alertDescriptor: alert,
alertPresentable: nil))
}
/// Presents an alert with a description on a context with custom configuration of the alert when a given condition is true.
@ -56,12 +53,9 @@ public extension View {
alertDescriptor descriptor: AlertDescriptor,
alertViewFactory: Closure<AlertDescriptor, AlertPresentable>) -> some View {
if isPresented.wrappedValue {
alertViewFactory(descriptor).present(on: context) {
isPresented.wrappedValue = false
}
}
return self
modifier(AlertModifier(isPresented: isPresented,
context: context,
alertDescriptor: descriptor,
alertPresentable: alertViewFactory(descriptor)))
}
}

View File

@ -27,8 +27,32 @@ Core UI elements: protocols, views and helpers.
Use to present alerts in a few lines of code. Can be used for UIKit and SwiftUI
> You can initialize `AlertsFactory` with your own *LocalizationProvider* or use `DefaultAlertLocalizationProvider`
### Your view or view controller must implement PresentationContext protocol
There are `UIKitAlertContext` protocol that are designed to make it easier to work with `AlertPresentationContext` protocol. By default, no changes need to be made for UIKit view controllers to make them conform to `UIKitAlertContext`.
### Your view or view controller must implement AlertPresentationContext protocol
The implementation of the protocol says that an alert can be shown from this object. Also there is a `UIKitAlertContext` protocol designed to make it easier to work with `AlertPresentationContext` protocol. By default, no changes need to be made for UIKit view controllers to make them conform to `UIKitAlertContext`.
### Your alert controller must implement AlertPresentable protocol
The implementation of this protocol says that an alert can be shown from the context. By default, the standard `UIAlertController` conforms to the protocol. Accordingly, when using a custom alert, it must also conform to the protocol:
```swift
import PopupDialog
extension PopupDialog: AlertPresentable {
@discardableResult
public func configured(with configuration: AlertDescriptor) -> Self {
title = configuration.title
for action in configuration.actions {
addButton(DefaultButton(title: action.title, action: action.action))
}
return self
}
public func present(on context: AlertPresentationContext, completion: VoidClosure?) {
context.present(self, animated: true, completion: completion)
}
}
```
## Custom alerts
```swift

View File

@ -36,7 +36,7 @@ open class AlertFactory {
/// - message: A text string used as the message of the alert.
/// - tint: A color used as a tint color of the alert. Default color is UIColor.systemBlue.
/// - actions: An array of actions of the alert.
/// - Returns: Alert descriptor, which can be used to present alert view.
/// - Returns: Alert descriptor which can be used to present alert view.
open func alert(title: String? = nil,
message: String? = nil,
tint: UIColor = .systemBlue,
@ -54,7 +54,7 @@ open class AlertFactory {
/// - message: A text string used as the message of the sheet alert.
/// - tint: A color used as a tint color of the sheet alert. Default color is UIColor.systemBlue.
/// - actions: An array of actions of the sheet alert.
/// - Returns: Alert descriptor, which can be used to present sheet alert view.
/// - Returns: Alert descriptor which can be used to present sheet alert view.
open func sheetAlert(title: String? = nil,
message: String? = nil,
tint: UIColor = .systemBlue,
@ -72,7 +72,7 @@ open class AlertFactory {
/// - title: A text string used as the title of the alert.
/// - message: A text string used as the message of the alert.
/// - tint: A color used as a tint color of the alert. Default color is UIColor.systemBlue.
/// - Returns: Alert descriptor, which can be used to present alert view.
/// - Returns: Alert descriptor which can be used to present alert view.
open func okAlert(title: String? = nil,
message: String? = nil,
tint: UIColor = .systemBlue) -> AlertDescriptor {
@ -89,7 +89,7 @@ open class AlertFactory {
/// - message: A text string used as the message of the alert.
/// - tint: A color used as a tint color of the alert. Default color is UIColor.systemBlue.
/// - retryAction: A closure called by tapping on the retry button of the alert.
/// - Returns: Alert descriptor, which can be used to present alert view.
/// - Returns: Alert descriptor which can be used to present alert view.
open func retryAlert(title: String? = nil,
message: String? = nil,
tint: UIColor = .systemBlue,
@ -111,7 +111,7 @@ open class AlertFactory {
/// - tint: A color used as a tint color of the alert. Default color is UIColor.systemBlue.
/// - yesAction: A closure called by tapping on the yes button of the alert.
/// - noAction: A closure called by tapping on the no button of the alert.
/// - Returns: Alert descriptor, which can be used to present alert view.
/// - Returns: Alert descriptor which can be used to present alert view.
open func dialogueAlert(title: String? = nil,
message: String? = nil,
tint: UIColor = .systemBlue,

View File

@ -23,7 +23,7 @@
import TISwiftUtils
import UIKit
// struct describe alert button information
/// A struct describes an alert button information
public struct AlertAction {
public let id = UUID()

View File

@ -22,7 +22,7 @@
import UIKit
/// Struct describes alert data
/// A struct describes alert data
public struct AlertDescriptor {
/// Alert title

View File

@ -22,6 +22,7 @@
import TISwiftUtils
/// A protocol represents an alert which can be presented on the given context
public protocol AlertPresentable {
func present(on context: AlertPresentationContext, completion: VoidClosure?)
}

View File

@ -23,6 +23,7 @@
import TISwiftUtils
import UIKit
/// A context from where the alert can be presented
public protocol AlertPresentationContext {
func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: VoidClosure?)
}

View File

@ -20,6 +20,4 @@
// THE SOFTWARE.
//
import UIKit
public protocol UIKitAlertContext: AlertPresentationContext { }

View File

@ -22,7 +22,7 @@
import Foundation
/// Provide default localization for alerts' buttons
/// Provides default localization for alerts' buttons
open class DefaultAlertLocalizationProvider: AlertLocalizationProvider {
public var bundle: Bundle