diff --git a/Dev/Brightroom.xcodeproj/project.pbxproj b/Dev/Brightroom.xcodeproj/project.pbxproj index 483ac437..95723ff4 100644 --- a/Dev/Brightroom.xcodeproj/project.pbxproj +++ b/Dev/Brightroom.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ 4B8CAF6925FFCD720075032A /* IsolatedEditingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8CAF6825FFCD720075032A /* IsolatedEditingView.swift */; }; 4B8CAF6E25FFCE720075032A /* FullscreenIdentifiableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8CAF6D25FFCE720075032A /* FullscreenIdentifiableView.swift */; }; 4B8CAF7325FFD13D0075032A /* BlurryMaskingViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8CAF7225FFD13D0075032A /* BlurryMaskingViewWrapper.swift */; }; + 4B9325832B6A923400DDABAC /* RotateScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9325822B6A923400DDABAC /* RotateScrollView.swift */; }; 4B9369E825F940E600B18571 /* nasa.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4B9369E725F940E600B18571 /* nasa.jpg */; }; 4B9369E925F940E600B18571 /* nasa.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4B9369E725F940E600B18571 /* nasa.jpg */; }; 4B98CCBC25EFF31300E4F61F /* SwiftUIDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B98CCBB25EFF31300E4F61F /* SwiftUIDemoApp.swift */; }; @@ -607,6 +608,7 @@ 4B8CAF6825FFCD720075032A /* IsolatedEditingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsolatedEditingView.swift; sourceTree = ""; }; 4B8CAF6D25FFCE720075032A /* FullscreenIdentifiableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenIdentifiableView.swift; sourceTree = ""; }; 4B8CAF7225FFD13D0075032A /* BlurryMaskingViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurryMaskingViewWrapper.swift; sourceTree = ""; }; + 4B9325822B6A923400DDABAC /* RotateScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotateScrollView.swift; sourceTree = ""; }; 4B9369E725F940E600B18571 /* nasa.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = nasa.jpg; sourceTree = ""; }; 4B98CCB925EFF31300E4F61F /* SwiftUIDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4B98CCBB25EFF31300E4F61F /* SwiftUIDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIDemoApp.swift; sourceTree = ""; }; @@ -1235,6 +1237,7 @@ 4B98CCC425EFF31400E4F61F /* Info.plist */, 4B98CCC125EFF31400E4F61F /* Preview Content */, 4BB9180B25F5460000C446B8 /* Launch Screen.storyboard */, + 4B9325822B6A923400DDABAC /* RotateScrollView.swift */, ); path = SwiftUIDemo; sourceTree = ""; @@ -2392,6 +2395,7 @@ 4B7EE66D25F515B400E17AF1 /* XCAssets+Generated.swift in Sources */, 4B98CCBE25EFF31300E4F61F /* ContentView.swift in Sources */, 4B75114025F13276002D804A /* DemoCropView.swift in Sources */, + 4B9325832B6A923400DDABAC /* RotateScrollView.swift in Sources */, 4B98CCBC25EFF31300E4F61F /* SwiftUIDemoApp.swift in Sources */, 4B98CCE725EFF4EF00E4F61F /* Mocks.swift in Sources */, ); diff --git a/Dev/Sources/SwiftUIDemo/ContentView.swift b/Dev/Sources/SwiftUIDemo/ContentView.swift index e5dd1ad2..ba3ab2e6 100644 --- a/Dev/Sources/SwiftUIDemo/ContentView.swift +++ b/Dev/Sources/SwiftUIDemo/ContentView.swift @@ -47,6 +47,8 @@ struct ContentView: View { Form { NavigationLink("Isolated", destination: IsolatedEditinView()) + NavigationLink("Hoge", destination: BookRotateScrollView()) + Section { Button("Component: Crop - keepAlive") { fullScreenView = .init { DemoCropView(editingStack: sharedStack) } diff --git a/Dev/Sources/SwiftUIDemo/DemoCropView.swift b/Dev/Sources/SwiftUIDemo/DemoCropView.swift index 4d478f88..6bc1fad5 100644 --- a/Dev/Sources/SwiftUIDemo/DemoCropView.swift +++ b/Dev/Sources/SwiftUIDemo/DemoCropView.swift @@ -16,6 +16,7 @@ struct DemoCropView: View { let editingStack: EditingStack @State var rotation: EditingCrop.Rotation? + @State var adjustmentAngle: EditingCrop.AdjustmentAngle = .zero var body: some View { VStack { @@ -37,17 +38,27 @@ struct DemoCropView: View { }) ) .rotation(rotation) + .adjustmentAngle(adjustmentAngle) .clipped() HStack { - Button("rotate") { + Button("0") { + self.rotation = .angle_0 + } + Button("90") { + self.rotation = .angle_90 + } + Button("180") { self.rotation = .angle_180 } + Button("270") { + self.rotation = .angle_270 + } Button("- 10") { - self.rotation = .angle_180 + self.adjustmentAngle -= .degrees(10) } Button("+ 10") { - self.rotation = .angle_180 + self.adjustmentAngle += .degrees(10) } } } @@ -66,3 +77,19 @@ struct DemoCropView: View { #Preview { DemoCropView(editingStack: Mocks.makeEditingStack(image: Mocks.imageHorizontal())) } + +#Preview { + Text("") + .onAppear { + + let uiView = UIView(frame: .init(origin: .zero, size: .init(width: 100, height: 200))) + print(uiView.frame) + + uiView.transform = .init(rotationAngle: Angle(degrees: 10).radians) + print(uiView.transform) + + print(uiView.frame, uiView.bounds) + } +} + + diff --git a/Dev/Sources/SwiftUIDemo/RotateScrollView.swift b/Dev/Sources/SwiftUIDemo/RotateScrollView.swift new file mode 100644 index 00000000..9b1f36db --- /dev/null +++ b/Dev/Sources/SwiftUIDemo/RotateScrollView.swift @@ -0,0 +1,57 @@ +import SwiftUI + +struct BookRotateScrollView: View, PreviewProvider { + var body: some View { + ContentView() + } + + static var previews: some View { + Self() + } + + private struct ContentView: View { + + var body: some View { + Represent() + } + } + + class ContainerView: UIView { + + override init(frame: CGRect) { + super.init(frame: frame) + + let scrollView = UIScrollView() + scrollView.backgroundColor = .black + scrollView.frame = frame.insetBy(dx: 30, dy: 30) + + addSubview(scrollView) + + scrollView.transform = .init(rotationAngle: Angle(degrees: 40).radians) + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + } + + struct Represent: UIViewRepresentable { + + func makeUIView(context: Context) -> BookRotateScrollView.ContainerView { + .init(frame: .init(origin: .zero, size: .init(width: 300, height: 300))) + } + + func updateUIView(_ uiView: BookRotateScrollView.ContainerView, context: Context) { + + } + + typealias UIViewType = ContainerView + + + + } + +} + diff --git a/Sources/BrightroomEngine/Core/EditingCrop.swift b/Sources/BrightroomEngine/Core/EditingCrop.swift index ebc89141..786884c2 100644 --- a/Sources/BrightroomEngine/Core/EditingCrop.swift +++ b/Sources/BrightroomEngine/Core/EditingCrop.swift @@ -21,6 +21,7 @@ import UIKit import Vision +import SwiftUI /// A representation of cropping extent in Image. public struct EditingCrop: Equatable { @@ -64,6 +65,8 @@ public struct EditingCrop: Equatable { } } + public typealias AdjustmentAngle = SwiftUI.Angle + /// The dimensions in pixel for the image. /// Applied image-orientation. public var imageSize: CGSize @@ -74,6 +77,9 @@ public struct EditingCrop: Equatable { /// The angle that specifies rotation for the image. public var rotation: Rotation = .angle_0 + /// An angle to rotate in addition to the specified rotation. + public var adjustmentAngle: AdjustmentAngle = .zero + public private(set) var scaleToRestore: CGFloat public init(from ciImage: CIImage) { diff --git a/Sources/BrightroomUI/Shared/Components/Crop/CropView.swift b/Sources/BrightroomUI/Shared/Components/Crop/CropView.swift index bb24688a..1fb48477 100644 --- a/Sources/BrightroomUI/Shared/Components/Crop/CropView.swift +++ b/Sources/BrightroomUI/Shared/Components/Crop/CropView.swift @@ -395,6 +395,14 @@ public final class CropView: UIView, UIScrollViewDelegate { } } + public func setAdjustmentAngle(_ angle: EditingCrop.AdjustmentAngle) { + + store.commit { + $0.proposedCrop?.adjustmentAngle = angle + $0.layoutVersion += 1 + } + } + public func setCrop(_ crop: EditingCrop) { _pixeleditor_ensureMainThread() @@ -522,33 +530,52 @@ extension CropView { size = aspectRatio.sizeThatFitsWithRounding(in: bounds.size) guideView.setLockedAspectRatio(preferredAspectRatio) case .angle_90: - size = aspectRatio.swapped().sizeThatFitsWithRounding(in: bounds.size) + size = aspectRatio + .swapped() + .sizeThatFitsWithRounding(in: bounds.size) guideView.setLockedAspectRatio(preferredAspectRatio?.swapped()) case .angle_180: size = aspectRatio.sizeThatFitsWithRounding(in: bounds.size) guideView.setLockedAspectRatio(preferredAspectRatio) case .angle_270: - size = aspectRatio.swapped().sizeThatFitsWithRounding(in: bounds.size) + size = aspectRatio + .swapped() + .sizeThatFitsWithRounding(in: bounds.size) guideView.setLockedAspectRatio(preferredAspectRatio?.swapped()) } - scrollView.transform = crop.rotation.transform - - scrollView.frame = .init( + let scrollViewFrame = CGRect( origin: .init( x: contentInset.left + ((bounds.width - size.width) / 2) /* centering offset */, y: contentInset.top + ((bounds.height - size.height) / 2) /* centering offset */ ), size: size ) - - scrollBackdropView.frame = scrollView.frame - } - applyLayoutDescendants: do { - guideView.frame = scrollView.frame + // step 1 - rotate in right angle + do { + + scrollView.transform = crop.rotation.transform + scrollView.frame = scrollViewFrame + + scrollBackdropView.frame = scrollViewFrame + guideView.frame = scrollViewFrame + + } + + // step 2 - rotate in adjustment angle + do { + let frame = scrollView.frame + let rotatedFrame = frame.applying(.init(rotationAngle: crop.adjustmentAngle.radians)) + scrollView.frame = rotatedFrame + scrollView.transform = crop.rotation.transform.rotated(by: crop.adjustmentAngle.radians) + } + + // step 3 - lay the scroll view out on thne center. + scrollView.center = .init(x: bounds.midX, y: bounds.midY) + } - + zoom: do { let (min, max) = crop.calculateZoomScale(scrollViewSize: scrollView.bounds.size) diff --git a/Sources/BrightroomUI/Shared/Components/Crop/SwiftUICropView.swift b/Sources/BrightroomUI/Shared/Components/Crop/SwiftUICropView.swift index 5dc5b7cb..1dfdd07d 100644 --- a/Sources/BrightroomUI/Shared/Components/Crop/SwiftUICropView.swift +++ b/Sources/BrightroomUI/Shared/Components/Crop/SwiftUICropView.swift @@ -60,6 +60,7 @@ public struct SwiftUICropView: UIViewControllerRepresentable { private let factory: () -> CropView private var _rotation: EditingCrop.Rotation? + private var _adjustmentAngle: EditingCrop.AdjustmentAngle? public init( editingStack: EditingStack, @@ -99,6 +100,10 @@ public struct SwiftUICropView: UIViewControllerRepresentable { if let _rotation { uiViewController.bodyView.setRotation(_rotation) } + + if let _adjustmentAngle { + uiViewController.bodyView.setAdjustmentAngle(_adjustmentAngle) + } } public func rotation(_ rotation: EditingCrop.Rotation?) -> Self { @@ -108,4 +113,12 @@ public struct SwiftUICropView: UIViewControllerRepresentable { return modified } + public func adjustmentAngle(_ angle: EditingCrop.AdjustmentAngle?) -> Self { + + var modified = self + modified._adjustmentAngle = angle + return modified + + } + }