I have tried some solutions from other posts but I haven't been able to solve the question I am going to post.
I have a custom UIView that draws a SoundWave and it contains a UIScrollView. The UIScrollView contains a main UIView that contains two other custom UIViews (marker left and right) and UIImage view where i render the sound wave.
private func initialize(){
print("\(logClassName) initialize in Frame: \(frame)")
//ScrollView
addSubview(scrollView)
scrollView.constraintToSuperViewEdges()
//WaveView
scrollView.addSubview(waveView)
waveView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
waveView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0).isActive = true
waveView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0).isActive = true
waveView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true
waveViewWidthConstraint = waveView.widthAnchor.constraint(equalToConstant: 1000)
NSLayoutConstraint.activate([waveViewWidthConstraint])
scrollView.contentSize = CGSize(width: waveViewWidthConstraint.constant, height: 0)
//SoundWaveImageView
waveView.addSubview(soundWaveImageView)
soundWaveImageView.constraintToSuperViewEdges()
//WaveView markers
//TimeMarker
waveView.addSubview(timeMarkerView)
timeMarkerView.topAnchor.constraint(equalTo: waveView.topAnchor).isActive = true
timeMarkerView.bottomAnchor.constraint(equalTo: waveView.bottomAnchor).isActive = true
timeMarkerView.widthAnchor.constraint(equalToConstant: 1).isActive = true
timerMarkerViewLeadingContraint = timeMarkerView.leadingAnchor.constraint(equalTo: waveView.leadingAnchor, constant: 20)
NSLayoutConstraint.activate([timerMarkerViewLeadingContraint])
//LeftMarker
waveView.addSubview(leftMarkerView)
leftMarkerView.topAnchor.constraint(equalTo: waveView.topAnchor).isActive = true
leftMarkerView.bottomAnchor.constraint(equalTo: waveView.bottomAnchor).isActive = true
leftMarkerView.widthAnchor.constraint(equalToConstant: leftMarkerView.triangleWidth).isActive = true
leftMarkerViewLeadingConstraint = leftMarkerView.leadingAnchor.constraint(equalTo: waveView.leadingAnchor, constant: 0)
NSLayoutConstraint.activate([leftMarkerViewLeadingConstraint])
//RightMarker
waveView.addSubview(rightMarkerView)
rightMarkerView.topAnchor.constraint(equalTo: waveView.topAnchor).isActive = true
rightMarkerView.bottomAnchor.constraint(equalTo: waveView.bottomAnchor).isActive = true
rightMarkerView.widthAnchor.constraint(equalToConstant: rightMarkerView.triangleWidth).isActive = true
rightMarkerViewLeadingConstraint = rightMarkerView.trailingAnchor.constraint(equalTo: waveView.leadingAnchor, constant: 50)
NSLayoutConstraint.activate([rightMarkerViewLeadingConstraint])
}
As seen, the main UIView is constraint to the edges of the UIScrollView and the width constraint is the total length which changes according the needs. The function responsible for that is the following:
private func updateWidthConstraintValue(_ newWidth:CGFloat){
waveViewWidthConstraint.constant = newWidth
scrollView.contentSize = CGSize(width: waveViewWidthConstraint.constant, height: 0)
}
When implementing the zoom for the UIScrollView it works but it zooms both vertically and horizontally (as expected) but I would like to implement only horizontally. I have this so far:
func scrollViewDidZoom(_ scrollView: UIScrollView) {
print("\(logClassName) TEST -> scrollView did zoom \(scrollView.zoomScale)")
expandHorizontally(withScale: scrollView.zoomScale)
//scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0)
//scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return waveView
}
private func expandHorizontally(withScale scale:CGFloat){
let scaledWidth = currentWaveViewWidth * scale
print("\(logClassName) TEST -> scaledWidth = \(scaledWidth)")
updateWidthConstraintValue(scaledWidth)
// Adjust content size and content offset
scrollView.contentSize = CGSize(width: waveViewWidthConstraint.constant,
height: 0);
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x,
y: scrollView.contentOffset.y);
}
The UI Variables are declared like that:
lazy private (set) var scrollView:UIScrollView = {
let rtView = UIScrollView()
rtView.translatesAutoresizingMaskIntoConstraints = false
rtView.backgroundColor = .clear
rtView.bounces = false
rtView.bouncesZoom = false
rtView.minimumZoomScale = 1
rtView.maximumZoomScale = 3
rtView.delegate = self
return rtView
}()
private (set) lazy var waveView:UIView = {
let rtView = UIView()
rtView.translatesAutoresizingMaskIntoConstraints = false
rtView.backgroundColor = .clear
rtView.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(waveViewDidTouch)))
return rtView
}()
private (set) var waveViewWidthConstraint = NSLayoutConstraint()
private (set) lazy var soundWaveImageView:UIImageView = {
let rtView = UIImageView()
rtView.translatesAutoresizingMaskIntoConstraints = false
return rtView
}()
private (set) lazy var timeMarkerView:UIView = {
let rtView = UIView()
rtView.translatesAutoresizingMaskIntoConstraints = false
rtView.backgroundColor = UIColor.AppColors.defaultTint
return rtView
}()
private var timerMarkerViewLeadingContraint = NSLayoutConstraint()
private (set) lazy var leftMarkerView:MarkerView = {
let rtView = MarkerView(title: AppHelper.printLocalized(withKey: "messages.start", targetSpecific: false), markerDirection: .right)
rtView.translatesAutoresizingMaskIntoConstraints = false
rtView.delegate = self
return rtView
}()
private var leftMarkerViewLeadingConstraint = NSLayoutConstraint()
private (set) lazy var rightMarkerView:MarkerView = {
let rtView = MarkerView(title: AppHelper.printLocalized(withKey: "messages.end", targetSpecific: false), markerDirection: .left)
rtView.translatesAutoresizingMaskIntoConstraints = false
rtView.delegate = self
return rtView
}()
private var rightMarkerViewLeadingConstraint = NSLayoutConstraint()
I have tried to put all the relevant information as it is a big class.
I have figure out a temporary solution thank you to LaurentMaquet. At some point I will post the final soundWaveView as it is in development at the moment.
The changes made are:
1- Create a custom class for soundWave.
class WaveView:UIView{
var unzoomedViewHeight: CGFloat = 0
override var transform: CGAffineTransform{
get{ return super.transform }
set{
var t = newValue
t.d = 1.0
t.ty = (1.0 - t.a) * unzoomedViewHeight/2
super.transform = t
}
}
override func layoutSubviews() {
super.layoutSubviews()
unzoomedViewHeight = frame.size.height
print("\(logClassName) did layout to \(AppHelper.traitStatus) -> frame = \(frame)")
}
}
2- Make my soundView var be a SoundView
private (set) lazy var waveView:WaveView = {
let rtView = WaveView()
rtView.translatesAutoresizingMaskIntoConstraints = false
rtView.backgroundColor = .clear
rtView.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(waveViewDidTouch)))
return rtView
}()
Result