简体   繁体   中英

SwiftUI View Sizing (Parent/Child Relationship)

I'm working though some examples to get familiar with SwiftUI and I am having an issue with sizing.

I'm used to the way UIKit handles resizing views, where with AutoLayout you can essentially make a parent size to it's children. This does not seem to be the case with SwiftUI.

Everything that I've read has shown how to use GeometryReader on the child to get the size of its parent is suggesting, and that the child can then choose it's own size, but I have not seen how to get the child's size from the parent.

Say I have the following code:

struct ContentView: View {
    var body: some View {
        Text("This is Text").ripple(rippleColor: .lightGray, rippleOpacity: 0.5)
    }
}

ripple() is an extension function that adds a custom ViewModifier that overlays the content onto a UIViewRepresentable view for the purposes of creating a material ripple effect when tapping.

struct RippleModifier: ViewModifier {
    let rippleColor: UIColor
    let rippleOpacity: Float

    func body(content: Content) -> some View {
        RippleView(
            rippleColor: rippleColor,
            rippleOpacity: rippleOpacity
        )
        .overlay(content)
    }
}

extension View {
    func ripple(rippleColor: UIColor, rippleOpacity: Float) -> some View {
        modifier(
            RippleModifier(
                rippleColor: rippleColor,
                rippleOpacity: rippleOpacity
            )
        )
    }
}

The issue I'm having is that when I just have the Text , it sets its frame to fit its content (the string). When I add the .ripple() modifier to it, the frame then becomes the entire screen.

With UIKit, I'd just add some constraints to pin what would be the UILabel to the parent, and I'd let that dictate the size of the parent.

Is there a way to do with with SwiftUI?

Bonus question:

Related but unrelated, how do I get the taps on the Text to fall through to the view it's overlaid on? The ripple will activate and properly animate when tapping anywhere within the frame of the view with the exception of tapping directly on the text itself.

Edit:

Here is partial code for the UIViewRepresentable view.

struct RippleView: UIViewRepresentable {
    var rippleColor: UIColor = .systemGray
    var rippleOpacity: Float = 0.5

    func makeUIView(context: Context) -> UIRippleView {
        UIRippleView()
    }

    func updateUIView(
        _ uiView: UIRippleView,
        context: UIViewRepresentableContext<RippleView>
    ) {
        uiView.rippleColor = rippleColor
        uiView.rippleOpacity = rippleOpacity
    }
}

open class UIRippleView: UIView {
    open var rippleColor: UIColor = .systemGray {
        didSet {
            rippleLayer.fillColor = rippleColor.cgColor
        }
    }

    open var rippleOpacity: Float = 0.5 {
        didSet {
            rippleLayer.opacity = rippleOpacity
        }
    }

    private lazy var rippleLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        layer.opacity = 0.0

        self.layer.insertSublayer(layer, at: 0)

        return layer
    }()

    public override init(frame: CGRect) {
        super.init(frame: frame)
        clipsToBounds = true
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        clipsToBounds = true
    }
}

There's an extension to that view that overrides the touch functions to calculate where the tap happened and to perform the animations, but that just modifies the path of the rippleLayer .

You can change overlay to background like this:

struct RippleModifier: ViewModifier {
    let rippleColor: UIColor
    let rippleOpacity: Float

    func body(content: Content) -> some View {
        content.background(
            RippleView(
                rippleColor: rippleColor,
                rippleOpacity: rippleOpacity
        ))
    }
}

So the RippleView gets the size constraint from the content and not the other way around.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM