Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update SwiftUICropView #224

Merged
merged 2 commits into from Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions Dev/Brightroom.xcodeproj/project.pbxproj
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
0451857F2B8F4A9E00A74713 /* DemoCropView2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0451857E2B8F4A9E00A74713 /* DemoCropView2.swift */; };
4B135F6E26136B5D003B5152 /* RendererOrientationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B135F6D26136B5D003B5152 /* RendererOrientationTests.swift */; };
4B2324E42B763D4A00662EB8 /* GeometryPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2324E32B763D4A00662EB8 /* GeometryPlayground.swift */; };
4B2A1107266B7B3000B0C885 /* test-image-10 in Resources */ = {isa = PBXBuildFile; fileRef = 4B2A10FA266B7B3000B0C885 /* test-image-10 */; };
Expand Down Expand Up @@ -568,6 +569,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
0451857E2B8F4A9E00A74713 /* DemoCropView2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoCropView2.swift; sourceTree = "<group>"; };
4B135F6D26136B5D003B5152 /* RendererOrientationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RendererOrientationTests.swift; sourceTree = "<group>"; };
4B2324E32B763D4A00662EB8 /* GeometryPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometryPlayground.swift; sourceTree = "<group>"; };
4B254FE6260BB32600F77E9A /* orientation_left_mirrored.HEIC */ = {isa = PBXFileReference; lastKnownFileType = file; path = orientation_left_mirrored.HEIC; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1254,6 +1256,7 @@
4B8CAF6825FFCD720075032A /* IsolatedEditingView.swift */,
4B8CAF7225FFD13D0075032A /* BlurryMaskingViewWrapper.swift */,
4B75113F25F13276002D804A /* DemoCropView.swift */,
0451857E2B8F4A9E00A74713 /* DemoCropView2.swift */,
4B98CCC425EFF31400E4F61F /* Info.plist */,
4B98CCC125EFF31400E4F61F /* Preview Content */,
4BB9180B25F5460000C446B8 /* Launch Screen.storyboard */,
Expand Down Expand Up @@ -2430,6 +2433,7 @@
4BA9896F2B70BF2000EB7983 /* RenderedResultView.swift in Sources */,
4B98CCBE25EFF31300E4F61F /* ContentView.swift in Sources */,
4B75114025F13276002D804A /* DemoCropView.swift in Sources */,
0451857F2B8F4A9E00A74713 /* DemoCropView2.swift in Sources */,
4B9325832B6A923400DDABAC /* RotateScrollView.swift in Sources */,
4B98CCBC25EFF31300E4F61F /* SwiftUIDemoApp.swift in Sources */,
4B98CCE725EFF4EF00E4F61F /* Mocks.swift in Sources */,
Expand Down
Expand Up @@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/FluidGroup/PrecisionLevelSlider",
"state" : {
"revision" : "6a6d1cbe962225d097012f47038f536c72e26c91",
"version" : "2.0.2"
"revision" : "3e460c1cea5ec916f7185948bc0a4e093e20f7a5",
"version" : "2.1.0"
}
},
{
Expand Down Expand Up @@ -140,8 +140,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/Verge.git",
"state" : {
"revision" : "790f1ef9af22f62fc7944f924ef4466dde071c2a",
"version" : "12.1.0"
"revision" : "a338c22b82c9a686a7131b54fa84ddad773f5c2a",
"version" : "12.0.0-beta.4"
}
}
],
Expand Down
110 changes: 110 additions & 0 deletions Dev/Sources/SwiftUIDemo/DemoCropView2.swift
@@ -0,0 +1,110 @@
//
// DemoCropView2.swift
// SwiftUIDemo
//
// Created by Jinsei Shima on 2024/02/28.
// Copyright 漏 2024 muukii. All rights reserved.
//

import BrightroomEngine
import BrightroomUI
import BrightroomUIPhotosCrop
import SwiftUI
import UIKit
import SwiftUISupport

struct DemoCropView2: View {

@StateObject var editingStack: EditingStack
@State var resultImage: ResultImage?

init(
editingStack: @escaping () -> EditingStack
) {
self._editingStack = .init(wrappedValue: editingStack())
}

var body: some View {
ZStack {

VStack {

Spacer()

SwiftUICropView(
editingStack: editingStack,
isGuideInteractionEnabled: false,
contentInset: .init(top: 20, left: 20, bottom: 20, right: 20),
cropInsideOverlay: { kind in
ViewHost(instantiated: CropView.RuleOfThirdsView(lineColor: .white))
.overlay {
Rectangle()
.fill(kind == nil ? Color.white : Color.white.opacity(0.6))
.overlay {
RoundedRectangle(cornerRadius: 24, style: .continuous)
.blendMode(.destinationOut)
}
.compositingGroup()
}
},
cropOutsideOverlay: { kind in
Rectangle()
.fill(kind == nil ? Color.white : Color.white.opacity(0.6))
}
)
.frame(height: 500)
.clipped()

Spacer()

}

VStack {
HStack {
Spacer()
Button("Done") {
let image = try! editingStack.makeRenderer().render().cgImage
self.resultImage = .init(cgImage: image)
}
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.tint(.yellow)
.foregroundColor(.black)
}
Spacer()
}
.padding(.horizontal, 30)
.padding(.vertical, 15)
.ignoresSafeArea()

}
.onAppear {
editingStack.start()
}
.sheet(item: $resultImage) {
RenderedResultView(result: $0)
}
}

}

#Preview("local") {
DemoCropView2(
editingStack: { Mocks.makeEditingStack(image: Mocks.imageHorizontal()) }
)
}

#Preview("remote") {
DemoCropView2(
editingStack: {
EditingStack(
imageProvider: .init(
editableRemoteURL: URL(
string:
"https://images.unsplash.com/photo-1604456930969-37f67bcd6e1e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1"
)!
)
)
}
)
}
Expand Up @@ -7,11 +7,12 @@
//

import UIKit
import SwiftUI

extension CropView {

open class CropOutsideOverlayBase: PixelEditorCodeBasedView {

open func didBeginAdjustment(kind: CropView.State.AdjustmentKind) {

}
Expand Down Expand Up @@ -74,4 +75,59 @@ extension CropView {
}
}

@available(iOS 14, *)
open class SwiftUICropOutsideOverlay<Content: View>: CropOutsideOverlayBase {

private let controller: UIHostingController<Container>
private let proxy: Proxy

public init(@ViewBuilder content: @escaping (CropView.State.AdjustmentKind?) -> Content) {

self.proxy = .init()
self.controller = .init(rootView: Container(proxy: proxy, content: content))

controller.view.backgroundColor = .clear
controller.view.preservesSuperviewLayoutMargins = false

super.init(frame: .zero)

addSubview(controller.view)
AutoLayoutTools.setEdge(controller.view, self)
}

open override func didBeginAdjustment(kind: CropView.State.AdjustmentKind) {
proxy.activeKind = kind
}

open override func didEndAdjustment(kind: CropView.State.AdjustmentKind) {
proxy.activeKind = nil
}

private final class Proxy: ObservableObject {

@Published var activeKind: CropView.State.AdjustmentKind?

}

private struct Container: View {

@ObservedObject var proxy: Proxy

private let content: (CropView.State.AdjustmentKind?) -> Content

public init(
proxy: Proxy,
content: @escaping (CropView.State.AdjustmentKind?) -> Content
) {
self.content = content
self.proxy = proxy
}

var body: some View {
content(proxy.activeKind)
}
}

}

}
35 changes: 30 additions & 5 deletions Sources/BrightroomUI/Shared/Components/Crop/SwiftUICropView.swift
Expand Up @@ -70,6 +70,8 @@ public struct SwiftUICropView: UIViewControllerRepresentable {
public typealias UIViewControllerType = _PixelEditor_WrapperViewController<CropView>

private let cropInsideOverlay: ((CropView.State.AdjustmentKind?) -> AnyView)?
private let cropOutsideOverlay: ((CropView.State.AdjustmentKind?) -> AnyView)?

private let editingStack: EditingStack

private var _rotation: EditingCrop.Rotation?
Expand All @@ -78,38 +80,61 @@ public struct SwiftUICropView: UIViewControllerRepresentable {
private var _resetAction: ResetAction?

private let stateHandler: @MainActor (Verge.Changes<CropView.State>) -> Void
private let isGuideInteractionEnabled: Bool
private let contentInset: UIEdgeInsets?

public init<InsideOverlay: View>(
public init<InsideOverlay: View, OutsideOverlay: View>(
editingStack: EditingStack,
isGuideInteractionEnabled: Bool = true,
contentInset: UIEdgeInsets? = nil,
@ViewBuilder cropInsideOverlay: @escaping (CropView.State.AdjustmentKind?) -> InsideOverlay,
@ViewBuilder cropOutsideOverlay: @escaping (CropView.State.AdjustmentKind?) -> OutsideOverlay,
stateHandler: @escaping @MainActor (Verge.Changes<CropView.State>) -> Void = { _ in }
) {
self.editingStack = editingStack
self.isGuideInteractionEnabled = isGuideInteractionEnabled
self.contentInset = contentInset
self.cropInsideOverlay = { AnyView(cropInsideOverlay($0)) }
self.cropOutsideOverlay = { AnyView(cropOutsideOverlay($0)) }
self.stateHandler = stateHandler
}

public init(
editingStack: EditingStack,
isGuideInteractionEnabled: Bool = true,
contentInset: UIEdgeInsets? = nil,
stateHandler: @escaping @MainActor (Verge.Changes<CropView.State>) -> Void = { _ in }
) {
self.cropInsideOverlay = nil
self.cropOutsideOverlay = nil
self.editingStack = editingStack
self.isGuideInteractionEnabled = isGuideInteractionEnabled
self.contentInset = contentInset
self.stateHandler = stateHandler
}

public func makeUIViewController(context: Context) -> _PixelEditor_WrapperViewController<CropView> {

let view = CropView(editingStack: editingStack)
let view: CropView
if let contentInset {
view = .init(editingStack: editingStack, contentInset: contentInset)
} else {
view = .init(editingStack: editingStack)
}

view.isAutoApplyEditingStackEnabled = true
view.isGuideInteractionEnabled = isGuideInteractionEnabled

let controller = _PixelEditor_WrapperViewController.init(bodyView: view)

if let cropInsideOverlay = cropInsideOverlay {
if let cropInsideOverlay {
view.setCropInsideOverlay(CropView.SwiftUICropInsideOverlay(content: cropInsideOverlay))
}

if let cropOutsideOverlay {
view.setCropOutsideOverlay(CropView.SwiftUICropOutsideOverlay(content: cropOutsideOverlay))
}

let controller = _PixelEditor_WrapperViewController.init(bodyView: view)

return controller
}

Expand Down
Expand Up @@ -212,7 +212,7 @@ public struct PhotosCropRotating: View {
source.rounded(.toNearestOrEven)
}
),
centerLevel: { value in
centerLevel: { value, _ in
HStack {
Spacer()
VStack {
Expand All @@ -225,7 +225,7 @@ public struct PhotosCropRotating: View {
}
.foregroundStyle(.tint)
},
track: { value in
track: { value, _ in
VStack {
HStack {
Spacer()
Expand Down