我遇到的问题是,当我在带有渐变填充的ScrollView上放置一个矩形时,scrollview停止对触摸做出反应。
目的是淡化滚动视图底部的项目,以免它们与父视图中的自定义底部导航视图冲突。
我尝试使用.frame修饰符使渐变高度仅位于底部四分之一左右,以希望不再阻止滚动视图,但是它没有用。
有人知道解决这个问题的方法吗?
import Foundation
import SwiftUI
import CoreData
struct TestView: View {
var managedObjectContext:NSManagedObjectContext
var spendings:FetchedResults<Spending>
var expenses:FetchedResults<Expense>
var settings:FetchedResults<Settings>
@State private var showAddSpending:Bool = false
@Binding var selection:Int
var body: some View{
VStack{
HStack{
Text("Spending").padding()
}.padding(.horizontal)
ZStack{
//List items
ScrollView{
ForEach(self.spendings) { spend in
//if(spend.currentMonth == true){
HStack{
// IS EXPANDED
if spend.isExpanded {
VStack{
HStack{
//NAME
if(spend.currentMonth){
Text("\(spend.name)")
.lineLimit(1)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.foregroundColor(Color .orange)
.onLongPressGesture {
spend.currentMonth.toggle()
}
} else {
Text("\(spend.name)")
.lineLimit(1)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.onLongPressGesture {
spend.currentMonth.toggle()
}
}
//AMOUNT
Text("\(spend.amount)")
.frame(minWidth: 0, maxWidth: 70, alignment: .trailing)
//DELETE
DeleteStyle(text: "multiply", symbol: true)
.onTapGesture {
self.managedObjectContext.delete(spend)
do {
try self.managedObjectContext.save()
}catch{
print(error)
}
}
}
VStack{
//CATEGORY
Text("Category: \(spend.category)")
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
.onTapGesture {
spend.isExpanded.toggle()
}
} else {
// ISNT EXPANDED
HStack{
//NAME
if(spend.currentMonth){
Text("\(spend.name)")
.lineLimit(1)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.foregroundColor(Color .orange)
.onLongPressGesture {
spend.currentMonth.toggle()
}
} else {
Text("\(spend.name)")
.lineLimit(1)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.onLongPressGesture {
spend.currentMonth.toggle()
}
}
Spacer()
//AMOUNT
Text("\(spend.amount)")
.frame(minWidth: 0, maxWidth: 70, alignment: .trailing)
//DELETE
DeleteStyle(text: "multiply", symbol: true).onTapGesture {
self.managedObjectContext.delete(spend)
do {
try self.managedObjectContext.save()
}catch{
print(error)
}
}
}
.onTapGesture {
spend.isExpanded.toggle()
}
}
}
}
.foregroundColor(Color (UIColor .secondaryLabel))
}.padding(.horizontal)
//Button to add new item
VStack{
Spacer()
HStack{
Spacer()
/*
Text("Total: £\(calculateTotalSpendingForCurrentMonth())")
.foregroundColor(.white)
.padding(15)
.background(Color .orange)
.cornerRadius(40)
*/
if spendings.isEmpty {
HStack{
Text("Record a spend")
Image(systemName: "arrow.right")
}
.foregroundColor(Color (UIColor .secondaryLabel))
.padding(.bottom, 90)
.padding(.horizontal, 40)
}
VStack{
Button(action: {
self.showAddSpending = true
}) {
NavStyle(text: "plus", symbol: true)
}.sheet(isPresented: $showAddSpending) {
AddSpendingView(managedObjectContext: self.managedObjectContext, spendings: self.spendings, expenses: self.expenses)
}
}
}
}
//Black Fade at bottom
VStack{
Spacer()
Rectangle()
.fill (
LinearGradient(gradient: Gradient(colors: [.clear, .black]),
startPoint: .center, endPoint: .bottom)
)
}
}
}.background(Color (UIColor.secondarySystemBackground))
}
}
答案 0 :(得分:1)
ScrollView
本身是透明的,因此您可以使用渐变作为背景,就像下面的示例一样
ScrollView {
ForEach(0..<100) { i in
Text("Item \(i)")
}
}
.background(LinearGradient(gradient: Gradient(colors: [.clear, .black]),
startPoint: .center, endPoint: .bottom))
对于叠加层,有时您可以将以下修饰符与值false
配合使用来进行触摸
public func allowsHitTesting(_ enabled: Bool) -> some View
但是对于ScrollView,即使它传递了轻击手势事件,也不能完全适用,因为滚动会被阻止。
通过Xcode 11.2.1 / iOS 13.2测试
答案 1 :(得分:1)
确实,SwiftUI View仍然不适合解决此问题。但是我找到了一个解决方案,没有我想要的那么漂亮。
首先,我尝试了1)通过UIViewRepresentable用SwiftUI-View包装UIView-gradient-overlay和2)在SwiftUI.body()中使用此包装器。但是结果与使用SwiftUI-View的结果相同。
因此,有一个反向解决方案-1)使用UIHostingController将SwiftUI内容转换为UIViewController,以及2)通过SwiftUI.body()中的UIViewControllerRepresentable使用此持有人控制器的转换
struct ContentView: View {
var body: some View {
UIViewControllerWrapper()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
====
struct UIViewControllerWrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MyViewController {
return MyViewController()
}
func updateUIViewController(_ uiViewController: MyViewController, context: Context) {}
}
====
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// SwiftUI content wrapper
let swiftUIContent = UIHostingController(rootView: SwiftUIContent())
swiftUIContent.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(swiftUIContent.view)
swiftUIContent.view.leadingAnchor.constraint(
equalTo: view.leadingAnchor).isActive = true
swiftUIContent.view.trailingAnchor.constraint(
equalTo: view.trailingAnchor).isActive = true
swiftUIContent.view.topAnchor.constraint(
equalTo: view.topAnchor).isActive = true
swiftUIContent.view.bottomAnchor.constraint(
equalTo: view.bottomAnchor).isActive = true
swiftUIContent.didMove(toParent: self)
// UIKit view overlay
let viewOverlay = UIView(frame: UIScreen.main.bounds)
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [UIColor.red.withAlphaComponent(0.5).cgColor, UIColor.green.withAlphaComponent(0.5).cgColor]
viewOverlay.layer.insertSublayer(gradient, at: 0)
viewOverlay.isUserInteractionEnabled = false
view.addSubview(viewOverlay)
}
}
====
struct SwiftUIContent: View{
var body: some View {
let body =
ZStack{
// ScrollView {
List{
ForEach(0..<100) { i in
Text("Item \(i)")
.gesture(TapGesture()
.onEnded({ _ in
print("Item \(i)")
}))
}
}
}
return body
}
}
=====
所以滚动和录音效果很好!!享受吧!
答案 2 :(得分:1)
理想情况下,可以使用overlay
在allowsHitTesting(false)
的滚动视图顶部显示渐变,以便用户仍然可以滚动,但这目前不起作用(可能是SwiftUI错误)。
但是我能够按照您的建议使用ZStack和frame
来完成这项工作。这是ScrollView
的直接替代品,其中包括一个额外的参数fadeDistance
,该参数确定淡出效果的高度(用于垂直滚动)和/或宽度(用于水平滚动):>
struct FadingScrollView<Content: View>: View {
let fadeDistance: CGFloat
let axes: Axis.Set
let showsIndicators: Bool
let content: Content
init(
fadeDistance: CGFloat,
_ axes: Axis.Set = .vertical,
showsIndicators: Bool = true,
@ViewBuilder content: () -> Content
) {
self.fadeDistance = fadeDistance
self.axes = axes
self.showsIndicators = showsIndicators
self.content = content()
}
var body: some View {
ZStack(alignment: .bottomTrailing) {
ScrollView(axes, showsIndicators: showsIndicators) {
// Pad the content depending on the axes so that bottom or trailing
// part of the content isn't faded when scrolling all the way to the end.
if axes == .vertical {
HStack(spacing: 0) {
content
Spacer()
}
Spacer(minLength: fadeDistance)
} else if axes == .horizontal {
HStack(spacing: 0) {
VStack(spacing: 0) {
content
Spacer()
}
Spacer(minLength: fadeDistance)
}
} else {
HStack(spacing: 0) {
content
Spacer(minLength: fadeDistance)
}
Spacer(minLength: fadeDistance)
}
}
if axes.contains(.vertical) {
fadeGradient(for: .vertical)
.frame(height: fadeDistance)
.allowsHitTesting(false) // Maybe Apple will make this work in the future
}
if axes.contains(.horizontal) {
fadeGradient(for: .horizontal)
.frame(width: fadeDistance)
.allowsHitTesting(false) // Maybe Apple will make this work in the future
}
}
}
private func fadeGradient(for axis: Axis) -> some View {
LinearGradient(
gradient: Gradient(colors: [
Color(.systemBackground).opacity(0),
Color(.systemBackground).opacity(1)
]),
startPoint: axis == .vertical ? .top : .leading,
endPoint: axis == .vertical ? .bottom : .trailing
)
}
}
答案 3 :(得分:1)
将 UIViewRepresentable
与 .disabled(true)
结合使用允许与带有叠加层的 ScrollView 进行交互。
struct OverlayWrapper<Content: View>: UIViewRepresentable {
var content: Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content()
}
func makeUIView(context: Context) -> UIView {
let uiView = UIView()
// Get reference to the SwiftUI view wrapped inside PagerScrollView
let child = UIHostingController(rootView: content)
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
// Get size of the child
let newSize = child.view.sizeThatFits(CGSize(width: screenWidth, height: screenHeight))
let frame = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
child.view.frame = frame
child.view.backgroundColor = UIColor.clear
uiView.frame = frame
uiView.backgroundColor = UIColor.clear
uiView.addSubview(child.view)
return uiView
}
func updateUIView(_ uiView: UIView, context: Context) {
// If the UI is not updated, updateUIView will not be called
}
}
使用 OverlayWrapper
的示例:
struct ExampleView: View {
var body: some View {
ZStack {
ScrollView(.vertical) {
VStack {
ForEach(0..<100) { i in
Text("\(i)")
}
}
}
OverlayWrapper() {
// This can be replaced with any SwiftUI view.
Rectangle()
.frame(width: 200, height: 200)
.foregroundColor(Color.blue.opacity(0.5))
}
.frame(width: 200, height: 200, alignment: .center)
.disabled(true)
}
}
}
答案 4 :(得分:0)
如果有人在看如何将滑动手势传递给 scrollView,.allowsHitTesting(false)
只会传递轻按手势。
ZStack {
ScrollView {
Text("Lorem ipsum dolor sit amet, consectetuer...")
}
LinearGradient(gradient: Gradient(colors: [Color.black,
Color.clear]),
startPoint: .top,
endPoint: .bottom)
.allowsHitTesting(false)
}
解决方案是使用 .mask
修饰符将所有手势传递给 scrollView
var body: some View {
ScrollView {
Text("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc")
.multilineTextAlignment(.center)
}
}
.mask(
LinearGradient(gradient: Gradient(colors: [Color.black,
Color.black,
Color.clear]),
startPoint: .top,
endPoint: .bottom)
)
.ignoresSafeArea(edges: .bottom)
}
}