Skip to content

Instantly share code, notes, and snippets.

@vargero
Last active August 5, 2021 17:58
Show Gist options
  • Save vargero/7a018471856be3be06ef45910f6203df to your computer and use it in GitHub Desktop.
Save vargero/7a018471856be3be06ef45910f6203df to your computer and use it in GitHub Desktop.
Custom Action Sheet View Controller (UIKit + SwiftUI)

Condider a new blank iOS project set to use Storyboard. Update the ViewController to match the above and add in the other files.

import SwiftUI
struct CustomActionSheet: View {
@State private var offset: CGFloat = UIScreen.main.bounds.height
@Binding var isShowing: Bool
private var maxWidth: CGFloat { UIScreen.main.bounds.width }
private var maxHeight: CGFloat { UIScreen.main.bounds.height }
init(isShowing: Binding<Bool>) {
_isShowing = isShowing
}
private var actionSheet: some View {
VStack(spacing: 7) {
Color.gray
.frame(width: 45, height: 6)
.cornerRadius(3)
HStack {
Spacer()
VStack {
Text("1")
Text("2")
Text("3")
Text("4")
}
Spacer()
}
.padding(.bottom, (UIApplication.shared.windows.last?.safeAreaInsets.bottom ?? 0) + 40)
.padding(.top, 20)
.padding(.horizontal, 0)
.background(Color.white)
}
}
var body: some View {
VStack {
Spacer()
actionSheet
.offset(y: self.offset)
.gesture(DragGesture()
.onChanged({ (value) in
if value.translation.height > 0 {
self.offset = value.location.y
}
}).onEnded({ (value) in
showActionSheet(self.offset <= 100)
}))
}
.frame(width: maxWidth, height: maxHeight)
.edgesIgnoringSafeArea(.all)
.onChange(of: isShowing, perform: { value in
showActionSheet(value)
})
.background((self.offset <= 100 ? Color.black.opacity(0.3) : Color.clear)
.edgesIgnoringSafeArea(.all)
.onTapGesture {
showActionSheet(false)
})
}
func showActionSheet(_ show: Bool) {
withAnimation {
offset = show ? 0 : maxHeight
}
}
}
import SwiftUI
import UIKit
class CustomActionSheetViewController: UIViewController {
private var internalVC: UIHostingController<CustomActionSheet>?
var show = true
lazy var isShowing: Binding<Bool> = .init {
self.show
} set: { show in
self.show = show
if !show {
self.dismiss(animated: false, completion: nil)
}
}
lazy var testView = CustomActionSheet(isShowing: isShowing)
override func viewDidLoad() {
super.viewDidLoad()
let ivc = UIHostingController(rootView: testView)
internalVC = ivc
view.addSubview(ivc.view)
ivc.view.backgroundColor = .clear
ivc.view.translatesAutoresizingMaskIntoConstraints = false
ivc.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
ivc.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
ivc.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
ivc.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
isShowing.wrappedValue = true
isShowing.update()
}
}
import UIKit
class ViewController: UIViewController {
let button = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.backgroundColor = .white
button.setTitle("TAP THIS BUTTON", for: .normal)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.addTarget(self, action: #selector(tapped), for: .touchUpInside)
}
@objc private func tapped() {
let vc = CustomActionSheetViewController()
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: false)
}
}
@vargero
Copy link
Author

vargero commented Aug 5, 2021

The view controller presenting this:

class ViewController: UIViewController {
    let button = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.backgroundColor = .white
        
        button.setTitle("TAP THIS BUTTON", for: .normal)
        
        view.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        
        button.addTarget(self, action: #selector(tapped), for: .touchUpInside)
        
        
    }
    
    @objc private func tapped() {
        let vc = CustomActionSheetViewController()
        vc.modalPresentationStyle = .overCurrentContext
        present(vc, animated: false)
    }


}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment