I am creating a link preview and in order to set a custom frame to the preview I have to override the intrinsic content size and place my own sizing. I pass frame.width and frame.height which is supposed to reflect .frame(maxWidth: 300, maxHeight: 400)
in the view.
If the preview is originally smaller that 300*400 I want it to remain its current size and if it's larger, then it should scale down to 300x400. The problem I'm facing is passing in frame.width and frame.height to the override var intrinsicContentSize
causes the preview to be scaled to 300x400, which is supposed to only be the max frame, not the default frame.
Thus smaller previews such as this SO thread are stretched to the size of 300x400. How can I modify this to not stretch previews while keeping the max frame?
I tried to pass in the code below but that didn't work. I also played around with .scaledToFill, .scaledToFit, .aspectRation(... fit/fill) but none did the trick.
return CGSize(width: super.intrinsicContentSize.width, height: super.intrinsicContentSize.height)
import SwiftUI
struct SwiftUIView: View {
@State var text = "https://stackoverflow.com/questions/77101513/resize-swift-ui-real-link-to-fit-frame/77102190?noredirect=1#comment135923413_77102190"
var body: some View {
if let url = checkForFirstUrl(text: text){
LinkPreviewView(url: url)
.frame(maxWidth: 300, maxHeight: 400)
.aspectRatio(contentMode: .fit)
}
}
}
struct LinkPreviewView: UIViewRepresentable {
let url: URL
func makeUIView(context: Context) -> UIView {
let linkView = CustomLinkView()
return linkView
}
func updateUIView(_ uiView: UIView, context: Context) {
let provider = LPMetadataProvider()
provider.startFetchingMetadata(for: url) { metaData, error in
guard let data = metaData, error == nil else { return }
DispatchQueue.main.async {
if let linkPreview = uiView as? CustomLinkView {
linkPreview.metadata = data
}
}
}
}
}
class CustomLinkView: LPLinkView {
init() {
super.init(frame: .zero)
}
override var intrinsicContentSize: CGSize {
return CGSize(width: frame.width, height: frame.height)
}
}
func checkForFirstUrl(text: String) -> URL? {
let types: NSTextCheckingResult.CheckingType = .link
do {
let detector = try NSDataDetector(types: types.rawValue)
let matches = detector.matches(in: text, options: .reportCompletion, range: NSMakeRange(0, text.count))
if let firstMatch = matches.first {
return firstMatch.url
}
} catch {
print("")
}
return nil
}
I found a fix by dynamically picking the size using linkView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize) here is the code:
import SwiftUI
struct SwiftUIView: View {
@State var text = "https://wp.titan.email/mail/"
@State var size: CGFloat = .zero
@State var size2: CGFloat = .zero
var body: some View {
VStack {
if let url = checkForFirstUrl(text: text){
LinkPreviewView(url: url, width: $size, height: $size2, message: true)
.frame(width: size, height: size2, alignment: .leading)
.aspectRatio(contentMode: .fill)
.cornerRadius(15)
}
}
}
}
import LinkPresentation
import UIKit
import SwiftUI
struct LinkPreviewView: UIViewRepresentable {
let url: URL
@Binding var width: CGFloat
@Binding var height: CGFloat
func makeUIView(context: Context) -> UIView {
let linkView = CustomLinkView()
let provider = LPMetadataProvider()
provider.startFetchingMetadata(for: url) { metaData, error in
guard let data = metaData, error == nil else { return }
DispatchQueue.main.async {
linkView.metadata = data
let linksize = linkView.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize)
let width = linksize.width
let height = linksize.height
let goal = widthOrHeight(width: true) * 0.8
if width > goal {
self.width = goal
} else {
self.width = width
}
if height > 400 {
self.height = 400
} else {
self.height = height
}
}
}
return linkView
}
func updateUIView(_ uiView: UIView, context: Context) { }
}
class CustomLinkView: LPLinkView {
init() {
super.init(frame: .zero)
}
override var intrinsicContentSize: CGSize {
return CGSize(width: frame.width, height: frame.height)
}
}
func checkForFirstUrl(text: String) -> URL? {
let types: NSTextCheckingResult.CheckingType = .link
do {
let detector = try NSDataDetector(types: types.rawValue)
let matches = detector.matches(in: text, options: .reportCompletion, range: NSMakeRange(0, text.count))
if let firstMatch = matches.first {
return firstMatch.url
}
} catch {
print("")
}
return nil
}