1

I tried to use the custom view button I created from UIKit to SwiftUI view using UIViewRepresentable but it did not work as expected.

Expected:

The view button will shrink upon tap or hold.

Expected_demo

Actual:

The view button expands when tap initially then shrink down when released

Actual_demo

The custom view button works properly in UIKit controllers.

Custom Button View Created in UIKit

public class CustomButton: UIView {

    fileprivate var clickedBtn: (() -> Void)?
.
.
.
.

    public func addAction(withEvent event: UIControl.Event = .touchUpInside, handler clickedHandler: @escaping () -> Void) {
        button.addTarget(self, action: #selector(holdRelease(_:)), for: .touchUpInside)
        button.addTarget(self, action: #selector(holdDown(_:)), for: .touchDown)
        button.addTarget(self, action: #selector(holdRelease(_:)), for: .touchDragExit)
        }
        clickedBtn = clickedHandler
    }
    
    @objc public func holdDown(_ sender: CXButton) {
        setupUIforHoldDown()
    }
    
    @objc public func holdRelease(_ sender: CXButton) {
        setupUIForHoldRelease()
        clickedBtn?()
    }
.
.
.
.
    
    /// Button rescales when touchDown. Button resizes back to 100% once release.
    private func setupUIforHoldDown() {
        rescaleButtonSize(scaleXto: 0.95, scaleYto: 0.95)
    }
    
    private func setupUIForHoldRelease() {
        rescaleButtonSize()
    }

    private func rescaleButtonSize(scaleXto x: CGFloat = 1.0,scaleYto y: CGFloat = 1.0) {
        DispatchQueue.main.async {
            UIView.animate(withDuration: 0.2, animations: {
                self.transform = CGAffineTransform(scaleX: x, y: y)
            })
        }
    }
}

UIViewRepresentable

import SwiftUI

struct SwiftUI_CustomButton: UIViewRepresentable {
    var title: String
    var handler: (() -> Void)? = nil
    
    func makeUIView(context: Context) -> CustomButton {
        let button = CustomButton()
        updateUIView(button, context: context)
        button.addAction {
            self.handler?()
        }
        return button
    }

    func updateUIView(_ button: CustomButton, context: Context) {
        button.title = title
    }
}

Swift UI View

struct CustomButton_SwiftUI: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            
            SwiftUI_CustomButton(
                title: "Actual"
            ) {
                // Button Action
            }
        }
        .padding(16)
    }
}

Edit: I'm still using UIKit custom view because it is also used on some UIKit controllers.

Dreiohc
  • 317
  • 2
  • 12

1 Answers1

1

You can do it without UIKit, using a custom ButtonStyle:

struct CustomButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(Color.accentColor)
            .scaleEffect(configuration.isPressed ? 0.95 : 1)
            .animation(.easeIn(duration: 0.2), value: configuration.isPressed)
    }
}

Usage:

Button(action: {}) {
    Text("Actual")
        .frame(height: 45)
        .frame(maxWidth: .infinity)
        .background(Color.black)
        .padding()
}.buttonStyle(CustomButtonStyle())

Result:

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • yeah this might be my last resort but the custom view is also used in UIKit controllers that is why I wanted it to work. Thanks Philip – Dreiohc Sep 14 '21 at 11:56
  • @Dreiohc in UIKit your code works fine, seems like some conflict between SwiftUI and UIKit animations. Seems like when you change view size with transform, SwiftUI changes it size to the original value. I was able to run animation by adding a container view and changing transform of this view. But also the size of this container shouldn't be updated in `layoutSubviews` during animation. I suggest you not mixing UIKit animations with SwiftUI ones, and use different buttons on different platforms. – Phil Dukhov Sep 14 '21 at 12:50
  • You saying this might be incompatbility issue with UIKit and SwiftUI when using transform? – Dreiohc Sep 14 '21 at 13:05
  • @Dreiohc exactly. – Phil Dukhov Sep 14 '21 at 16:10