Как реализовать слайдер изображений «до и после» в SwiftUI? Перерыл весь интернет, но проблема так и не решена. У меня есть реализация, но она работает неправильно. Когда вы начинаете перемещать ползунок, он не совпадает с фотографией должным образом. Вторая проблема в том, что маска работает неправильно. У меня есть капсула с текстом над фотографией, и на этикетке «до» есть капсула, но по какой-то причине ее нет на этикетке «после».
import SwiftUI
struct ContentView: View {
@State private var location: CGPoint = CGPoint(x: 0, y: 0)
@State private var maskWidth: CGFloat = 0.0
@State var startPoint: CGFloat = 0
@State var endPoint: CGFloat = 0
@State var yPoint: CGFloat = 0
var sliderWidth: CGFloat = 30
var containerWidth: CGFloat = 400
var containerHeight: CGFloat = 300
var body: some View {
ZStack {
ZStack {
Image(.boyPic)
.resizable()
.frame(width: containerWidth, height: containerHeight)
.clipped()
.overlay {
ZStack {
Capsule()
.frame(width: 64, height: 30)
.foregroundStyle(Color.black.opacity(0.6))
Text(BeforeAfterType.before.rawValue)
.foregroundStyle(Color.whiteColor)
.fonts(.medium, .smallTitleSize13)
}
.horAlig(.leading)
.verAlig(.top)
.padding(ARSizesType.smallPaddingSize.sizes)
}
Image(.boyPic)
.resizable()
.frame(width: containerWidth, height: containerHeight)
.clipped()
.overlay {
ZStack {
Capsule()
.frame(width: 64, height: 30)
.foregroundStyle(Color.black.opacity(0.6))
Text(BeforeAfterType.after.rawValue)
.foregroundStyle(Color.whiteColor)
.fonts(.medium, .smallTitleSize13)
}
.horAlig(.trailing)
.verAlig(.top)
.padding(ARSizesType.smallPaddingSize.sizes)
}
.mask(mask)
}
Slider
}
.frame(width: containerWidth, height: containerHeight)
.onAppear {
yPoint = containerHeight/2
location = CGPoint(x: containerWidth/2, y: yPoint)
maskWidth = containerWidth/2
endPoint = containerWidth
}
}
var dragAction: some Gesture {
DragGesture()
.onChanged { value in
updateDragView(point: value.location)
updateMaskView(point: value.translation)
}
.onEnded { value in
setInitialPosition()
}
}
var mask: some View {
HStack {
Spacer()
Rectangle()
.mask(Color.black)
.frame(width: maskWidth, height: containerHeight)
}
}
var Slider: some View {
ZStack {
Rectangle()
.fill(Color.white)
.frame(width: 2)
Image(.sliderButton)
.resizable()
.scaledToFill()
.foregroundColor(.white)
.frame(width: 55, height: 35)
.verAlig(.bottom, 50)
}
.position(location)
.gesture(dragAction)
.shadow(radius: 4)
}
func updateDragView(point: CGPoint) {
let locX = point.x
if locX > startPoint && locX < endPoint {
self.location = CGPoint(x: point.x, y: yPoint)
}
}
func updateMaskView(point: CGSize) {
let width = -(point.width)
let newWidth = ((containerWidth/2) + width)
if newWidth > 0 {
maskWidth = ((containerWidth/2) + width)
} else {
setInitialPosition()
}
}
func setInitialPosition() {
withAnimation {
location = CGPoint(x: containerWidth/2, y: yPoint)
maskWidth = containerWidth/2
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Ваш исходный код очень длинный, но вот что вам следует сделать:
struct BeforeAfterSlider: View {
@State private var percentage = 0.0
var body: some View {
ZStack { // 👈 Stacks image on top of each other
Image(.before)
Image(.after)
.mask { // 👈 Mask the top one
Rectangle() // 👈 Use the desired shape for the mask
.scaleEffect(
CGSize(
width: percentage, // 👈 Control the width of the mask
height: 1.0
),
anchor: .leading
)
}
}
.overlay(alignment: .bottom) {
Slider(value: $percentage, in: 0...1) // 👈 Connect the mask to your slider
}
}
}