How do you get a volume slider working in SwiftUI. Apple's tutorials provide help in regards to incorporating UIKit into a SwiftUI view (https://developer.apple.com/tutorials/swiftui/creating-and-combining-views#create-a-custom-image-view) but nothing that I can find on the volume slider. Below is the code I've got to this point largely taken from my Storyboard slider and adjusted, but it's not producing anything.
import SwiftUI
import UIKit
import MediaPlayer
struct VolumeView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
UIView(frame: .init(x: 50, y: 100, width: 100, height: 100))
}
func updateUIView(_ uiView: UIView, context: Context) {
setupVolumeSlider()
}
func setupVolumeSlider() {
var mpVolumeSlider: UISlider?
let volumeParentView: UIView! = UIView()
for subview in MPVolumeView().subviews {
guard let volumeSlider = subview as? UISlider else { continue }
mpVolumeSlider = volumeSlider
}
guard mpVolumeSlider != nil else { return }
volumeParentView.addSubview(mpVolumeSlider!)
}
}
A lot easier than I was making it out to be in the end:
import SwiftUI
import MediaPlayer
import UIKit
struct HomeView: UIViewRepresentable {
func makeUIView(context: Context) -> MPVolumeView {
MPVolumeView(frame: .zero)
}
func updateUIView(_ view: MPVolumeView, context: Context) {
}
}
Related
Because I need to know whether user taps with finger or pencil, I had to create a UIViewRepresentable with a UITapGestureRecognizer. Everything works fine when this View is used with other SwiftUI views. But when stack 2 of this view, and I clip the top one, the other one never catches any tap event.
Here is my custom view:
struct UIKitView: UIViewRepresentable {
let color: UIColor
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = color
let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(gesture:)))
view.addGestureRecognizer(tapGesture)
return view
}
func updateUIView(_ uiView: UIView, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(color: color)
}
class Coordinator: NSObject {
let color: UIColor
init(color: UIColor) {
self.color = color
}
#objc func handleTap(gesture: UITapGestureRecognizer) {
print("Tapped in \(color == .yellow ? "yellow" : "red")")
}
}
}
And here how it is used in SwiftUI View:
ZStack {
UIKitView(color: .yellow)
UIKitView(color: .red)
.clipShape(Circle())) // not required, just visual
.contentShape(Circle()))
}
The tap is always catched by the red view.
Then I've overriden the hitTest() function of my UIVIewRepresentable to check if the tap is inside the clipîng shape. The tap is then detected in the red circle, but nothing tiggers anymore outside.
Any idea why?
You can override this method in CircleView:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let center = CGPoint(x: bounds.size.width/2, y: bounds.size.height/2)
return pow(center.x-point.x, 2) + pow(center.y - point.y, 2) <= pow(bounds.size.width/2, 2)
}
Source
FYI there is also a mistake in your UIViewRepresentable, you need this
func updateUIView(_ uiView: UIView, context: Context) {
view.backgroundColor = color
}
I want to use pencil kit in my swiftui app. I encapsulate PKCanvasView in UIViewRepresentable. I can draw on canvas with only one stroke. However, the image will disappear immediately when I release the finger.
Am I missing anything?
import SwiftUI
import PencilKit
struct ContentView: View {
var body: some View {
CanvasView()
}
}
struct CanvasView: UIViewRepresentable {
func makeUIView(context: Context) -> PKCanvasView {
let canvas = PKCanvasView()
canvas.tool = PKInkingTool(.pen, color: .green, width: 10)
canvas.drawingPolicy = .anyInput
return canvas
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
}
}
EDIT:
Xcode14.1, simulator iOS16.1
I am trying to build out an application with the ability to use both apple pencil and finger input to draw. However, when I set the width of the tool to a sufficiently small number (1) the pencil input displays correctly, but when I use my finger it draws thicker lines. I would like uniformity in width when switching between pencil and finger. Any help would be appreciated. Bellow is the minimal reproducible code.
import Foundation
import SwiftUI
import PencilKit
struct ContentView: View {
var body: some View {
CanvasRepresentable()
}
}
struct CanvasRepresentable: UIViewRepresentable {
class Coordinator: NSObject, PKCanvasViewDelegate {
var parent: CanvasRepresentable
init(_ parent: CanvasRepresentable) {
self.parent = parent
}
func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIView(context: Context) -> PKCanvasView {
let c = PKCanvasView()
c.isOpaque = false
c.allowsFingerDrawing = true
c.delegate = context.coordinator
c.tool = PKInkingTool(.pen, color: .black, width: 1)
return c
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
let tool = uiView.tool as! PKInkingTool
print(tool.width)
}
}
Here is my code:
import SwiftUI
import FSCalendar
class calendars: UIViewController, FSCalendarDelegate{
var calendar = FSCalendar()
override func viewDidLoad() {
super.viewDidLoad()
calendar.delegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
calendar.frame = CGRect(x: 0, y: 100, width: view.frame.size.width, height: view.frame.size.width)
view.addSubview(calendar)
}
}
struct CalendarView: View {
var body: some View{
calendars()
}
}
I don't really know is it correct to swiftui but I got an error said Return type of property 'body' requires that 'calendars' conform to 'View'
I just wanna import a calendar and some kind of to do list.
The simplest way to include FSCalendar into SwiftUI is to wrap it in an UIView:
struct CalendarView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
return FSCalendar(frame: CGRect(x: 0.0, y: 40.0, width: .infinity, height: 300.0))
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
Usage in SwiftUI code:
CalendarView()
I got it..
struct CalendarController: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<CalendarController>) -> calendars {
return calendars()
}
func updateUIViewController(_ uiViewController: calendars, context: UIViewControllerRepresentableContext<CalendarController>) {
}
}
No Public API in SwiftUI to response for the resizable modifier of View protocol. Only Image in SwiftUI could work with .resizable(). Custom UIView like UIView for GIF is not resizable now.
I use SDWebImageSwiftUI AnimatedImage, which is backing UIKit View SDAnimatedImageView. AnimatedImage is not response to .resizable(), .scaleToFit, .aspectRatio(contentMode: .fit), etc. WebImage is backing SwiftUI Image, so it's working fine.
import SwiftUI
import SDWebImageSwiftUI
struct ContentView: View {
let url = URL(string: "https://media.giphy.com/media/H62DGtBRwgbrxWXh6t/giphy.gif")!
var body: some View {
VStack {
AnimatedImage(url: url)
.scaledToFit()
.frame(width: 100, height: 100)
WebImage(url: url)
.scaledToFit()
.frame(width: 100, height: 100)
}
}
}
Not sure if it's an Apple bug. Expect custom view like SDWebImageSwiftUI AnimatedImage is responsive to SwiftUI size related modifiers like .scaledToFit().
Related issue: https://github.com/SDWebImage/SDWebImageSwiftUI/issues/3
SwiftUI uses the compression resistance priority and the content hugging priority to decide what resizing is possible.
If you want to resize a view below its intrinsic content size, you need to reduce the compression resistance priority.
Example:
func makeUIView(context: Context) -> UIView {
let imageView = UIImageView(image: UIImage(named: "yourImage")!)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
return imageView
}
This will allow you to set .frame(width:height:) to any size you want.
Finally found a solution.
Make a UIView wrapper outside of the SDAnimationImageView or UIImageView, then override layoutSubviews() set the frame of subview.
Here is full code by me.
And SDWebImageSwiftUI also release a new version which uses wrapper to solve this problem.
class ImageModel: ObservableObject {
#Published var url: URL?
#Published var contentMode: UIView.ContentMode = .scaleAspectFill
}
struct WebImage: UIViewRepresentable {
#ObservedObject var imageModel = ImageModel()
func makeUIView(context: UIViewRepresentableContext<WebImage>) -> ImageView {
let uiView = ImageView(imageModel: imageModel)
return uiView
}
func updateUIView(_ uiView: ImageView, context: UIViewRepresentableContext<WebImage>) {
uiView.imageView.sd_setImage(with: imageModel.url)
uiView.imageView.contentMode = imageModel.contentMode
}
func url(_ url: URL?) -> Self {
imageModel.url = url
return self
}
func scaledToFit() -> Self {
imageModel.contentMode = .scaleAspectFit
return self
}
func scaledToFill() -> Self {
imageModel.contentMode = .scaleAspectFill
return self
}
}
class ImageView: UIView {
let imageView = UIImageView()
init(imageModel: ImageModel) {
super.init(frame: .zero)
addSubview(imageView)
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = bounds
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}