diff --git a/Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/ButtonPreview.swift b/Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/ButtonPreview.swift index 19dc015b..5a9ce2bd 100644 --- a/Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/ButtonPreview.swift +++ b/Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/ButtonPreview.swift @@ -35,6 +35,11 @@ struct ButtonPreview: View { Text("Leading").tag(ButtonVM.ImageLocation.leading) Text("Trailing").tag(ButtonVM.ImageLocation.trailing) } + Picker("Image Rendering Mode", selection: self.$model.imageRenderingMode) { + Text("Default").tag(Optional.none) + Text("Template").tag(ImageRenderingMode.template) + Text("Original").tag(ImageRenderingMode.original) + } Picker("Image Source", selection: self.$model.imageSrc) { Text("SF Symbol").tag(ButtonVM.ImageSource.sfSymbol("camera.fill")) Text("Local").tag(ButtonVM.ImageSource.local("avatar_placeholder")) diff --git a/Sources/ComponentsKit/Components/Avatar/Models/AvatarVM.swift b/Sources/ComponentsKit/Components/Avatar/Models/AvatarVM.swift index 2d7418b0..b946e1ed 100644 --- a/Sources/ComponentsKit/Components/Avatar/Models/AvatarVM.swift +++ b/Sources/ComponentsKit/Components/Avatar/Models/AvatarVM.swift @@ -95,7 +95,7 @@ extension AvatarVM { self.placeholderBackgroundColor.setFill() UIBezierPath(rect: CGRect(origin: .zero, size: size)).fill() - icon?.withTintColor(self.placeholderForegroundColor, renderingMode: .alwaysOriginal).draw(in: CGRect( + icon?.withTintColor(self.placeholderForegroundColor).draw(in: CGRect( x: (size.width - iconSize.width) / 2, y: (size.height - iconSize.height) / 2, width: iconSize.width, diff --git a/Sources/ComponentsKit/Components/Button/Models/ButtonImageSource.swift b/Sources/ComponentsKit/Components/Button/Models/ButtonImageSource.swift index c9598303..f672cc0a 100644 --- a/Sources/ComponentsKit/Components/Button/Models/ButtonImageSource.swift +++ b/Sources/ComponentsKit/Components/Button/Models/ButtonImageSource.swift @@ -1,7 +1,7 @@ import Foundation -/// Defines the image source options for a button. extension ButtonVM { + /// Defines the image source options for a button. public enum ImageSource: Hashable { /// An image loaded from a system SF Symbol. /// diff --git a/Sources/ComponentsKit/Components/Button/Models/ButtonVM.swift b/Sources/ComponentsKit/Components/Button/Models/ButtonVM.swift index bd9242fb..9303de5e 100644 --- a/Sources/ComponentsKit/Components/Button/Models/ButtonVM.swift +++ b/Sources/ComponentsKit/Components/Button/Models/ButtonVM.swift @@ -30,6 +30,9 @@ public struct ButtonVM: ComponentVM { /// Defaults to `.leading`. public var imageLocation: ImageLocation = .leading + /// Defines how image is rendered. + public var imageRenderingMode: ImageRenderingMode? + /// The source of the image to be displayed. public var imageSrc: ImageSource? @@ -164,10 +167,18 @@ extension ButtonVM { case .minimal: return 0 case .light, .filled, .bordered, .plain: - return switch self.size { - case .small: 16 - case .medium: 20 - case .large: 24 + if self.title.isNotEmpty || self.isLoading { + return switch self.size { + case .small: 16 + case .medium: 20 + case .large: 24 + } + } else { + return switch self.size { + case .small: 8 + case .medium: 10 + case .large: 12 + } } } } @@ -176,15 +187,14 @@ extension ButtonVM { extension ButtonVM { var image: UIImage? { guard let imageSrc else { return nil } - switch imageSrc { + + let image = switch imageSrc { case .sfSymbol(let name): - return UIImage(systemName: name)?.withTintColor( - self.foregroundColor.uiColor, - renderingMode: .alwaysOriginal - ) + UIImage(systemName: name) case .local(let name, let bundle): - return UIImage(named: name, in: bundle, compatibleWith: nil) + UIImage(named: name, in: bundle, compatibleWith: nil) } + return image?.withRenderingMode(self.imageRenderingMode) } } diff --git a/Sources/ComponentsKit/Components/Button/SUButton.swift b/Sources/ComponentsKit/Components/Button/SUButton.swift index 80cf0e89..63399930 100644 --- a/Sources/ComponentsKit/Components/Button/SUButton.swift +++ b/Sources/ComponentsKit/Components/Button/SUButton.swift @@ -59,19 +59,31 @@ public struct SUButton: View { SULoading(model: self.model.preferredLoadingVM) Text(self.model.title) case (false, let uiImage?, .leading) where self.model.title.isEmpty: - ButtonImageView(image: uiImage) - .frame(width: self.model.imageSide, height: self.model.imageSide) + ButtonImageView( + image: uiImage, + tintColor: self.model.foregroundColor.uiColor + ) + .frame(width: self.model.imageSide, height: self.model.imageSide) case (false, let uiImage?, .leading): - ButtonImageView(image: uiImage) - .frame(width: self.model.imageSide, height: self.model.imageSide) + ButtonImageView( + image: uiImage, + tintColor: self.model.foregroundColor.uiColor + ) + .frame(width: self.model.imageSide, height: self.model.imageSide) Text(self.model.title) case (false, let uiImage?, .trailing) where self.model.title.isEmpty: - ButtonImageView(image: uiImage) - .frame(width: self.model.imageSide, height: self.model.imageSide) + ButtonImageView( + image: uiImage, + tintColor: self.model.foregroundColor.uiColor + ) + .frame(width: self.model.imageSide, height: self.model.imageSide) case (false, let uiImage?, .trailing): Text(self.model.title) - ButtonImageView(image: uiImage) - .frame(width: self.model.imageSide, height: self.model.imageSide) + ButtonImageView( + image: uiImage, + tintColor: self.model.foregroundColor.uiColor + ) + .frame(width: self.model.imageSide, height: self.model.imageSide) case (false, _, _): Text(self.model.title) } @@ -88,16 +100,20 @@ private struct ButtonImageView: UIViewRepresentable { } let image: UIImage + let tintColor: UIColor func makeUIView(context: Context) -> UIImageView { let imageView = InternalImageView() imageView.image = self.image + imageView.tintColor = self.tintColor imageView.contentMode = .scaleAspectFit + imageView.isUserInteractionEnabled = true return imageView } func updateUIView(_ imageView: UIImageView, context: Context) { imageView.image = self.image + imageView.tintColor = self.tintColor } } @@ -108,10 +124,10 @@ private struct CustomButtonStyle: SwiftUI.ButtonStyle { configuration.label .font(self.model.preferredFont.font) .lineLimit(1) - .contentShape(.rect) .padding(.horizontal, self.model.horizontalPadding) .frame(maxWidth: self.model.width) .frame(height: self.model.height) + .contentShape(.rect) .foregroundStyle(self.model.foregroundColor.color) .background(self.model.backgroundColor?.color ?? .clear) .clipShape( diff --git a/Sources/ComponentsKit/Components/Button/UKButton.swift b/Sources/ComponentsKit/Components/Button/UKButton.swift index e7b63552..9653de56 100644 --- a/Sources/ComponentsKit/Components/Button/UKButton.swift +++ b/Sources/ComponentsKit/Components/Button/UKButton.swift @@ -250,6 +250,8 @@ extension UKButton { imageView.image = model.image imageView.contentMode = .scaleAspectFit imageView.isHidden = model.isLoading || model.imageSrc.isNil + imageView.tintColor = model.foregroundColor.uiColor + imageView.isUserInteractionEnabled = true } } } diff --git a/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationModifier.swift b/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationModifier.swift index 9b0eebc7..0d99bfa0 100644 --- a/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationModifier.swift +++ b/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationModifier.swift @@ -38,7 +38,10 @@ struct ModalPresentationModifier: ViewModifier { } } .fullScreenCover( - isPresented: self.$isPresented, + isPresented: .init( + get: { self.isPresented }, + set: { self.isContentVisible = $0 } + ), onDismiss: self.onDismiss, content: { self.content() diff --git a/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationWithItemModifier.swift b/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationWithItemModifier.swift index 0ad9a283..d6b6eed8 100644 --- a/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationWithItemModifier.swift +++ b/Sources/ComponentsKit/Components/Modal/SwiftUI/Helpers/ModalPresentationWithItemModifier.swift @@ -39,7 +39,10 @@ struct ModalPresentationWithItemModifier: ViewM } } .fullScreenCover( - item: self.$presentedItem, + item: .init( + get: { self.presentedItem }, + set: { self.visibleItem = $0 } + ), onDismiss: self.onDismiss, content: { item in self.content(item) diff --git a/Sources/ComponentsKit/Components/Modal/SwiftUI/SUBottomModal.swift b/Sources/ComponentsKit/Components/Modal/SwiftUI/SUBottomModal.swift index 8323bc39..1d062a7e 100644 --- a/Sources/ComponentsKit/Components/Modal/SwiftUI/SUBottomModal.swift +++ b/Sources/ComponentsKit/Components/Modal/SwiftUI/SUBottomModal.swift @@ -309,6 +309,7 @@ extension View { return self.bottomModal( item: item, model: model, + onDismiss: onDismiss, header: { _ in EmptyView() }, body: body, footer: { _ in EmptyView() } diff --git a/Sources/ComponentsKit/Components/Modal/SwiftUI/SUCenterModal.swift b/Sources/ComponentsKit/Components/Modal/SwiftUI/SUCenterModal.swift index 610a4933..39b54503 100644 --- a/Sources/ComponentsKit/Components/Modal/SwiftUI/SUCenterModal.swift +++ b/Sources/ComponentsKit/Components/Modal/SwiftUI/SUCenterModal.swift @@ -271,6 +271,7 @@ extension View { return self.centerModal( item: item, model: model, + onDismiss: onDismiss, header: { _ in EmptyView() }, body: body, footer: { _ in EmptyView() } diff --git a/Sources/ComponentsKit/Shared/Types/ImageRenderingMode.swift b/Sources/ComponentsKit/Shared/Types/ImageRenderingMode.swift new file mode 100644 index 00000000..b4b8f662 --- /dev/null +++ b/Sources/ComponentsKit/Shared/Types/ImageRenderingMode.swift @@ -0,0 +1,50 @@ +import SwiftUI +import UIKit + +/// A type that indicates how images are rendered. +public enum ImageRenderingMode { + /// A mode that renders all non-transparent pixels as the foreground + /// color. + case template + /// A mode that renders pixels of bitmap images as-is. + /// + /// For system images created from the SF Symbol set, multicolor symbols + /// respect the current foreground and accent colors. + case original +} + +// MARK: - UIKit Helpers + +extension ImageRenderingMode { + var uiImageRenderingMode: UIImage.RenderingMode { + switch self { + case .template: + return .alwaysTemplate + case .original: + return .alwaysOriginal + } + } +} + +extension UIImage { + func withRenderingMode(_ mode: ImageRenderingMode?) -> UIImage { + if let mode { + return self.withRenderingMode(mode.uiImageRenderingMode) + } else { + return self + } + } +} + +// MARK: - SwiftUI Helpers + +extension ImageRenderingMode { + var imageRenderingModel: Image.TemplateRenderingMode { + switch self { + case .template: + return .template + case .original: + return .original + } + } +}