Dreamio turn document
Improve Center Play/Pause Indicator Contrast
Improved the native player center play/pause flash so it remains visible over varied video content.
Summary
Improved the native player center play/pause flash so it remains visible over varied video content.
Changes Made
- Made the center play/pause flash larger, darker, and more defined with a stronger border and shadow.
- Kept the indicator visible for longer during the flash animation and added a reduced-motion fade path.
Context
The center-screen play/pause indicator was too transparent, making it hard to see against bright or busy video frames.
Important Implementation Details
- The existing Liquid Glass-style icon button remains in use, but the transient center instance now gets a high-contrast black backing instead of the low-alpha shared control treatment.
- The hit target and visual footprint increased from 68 to 80 points while keeping the indicator centered.
- Reduced Motion users now still receive a non-scaling visible flash instead of skipping the indicator entirely.
Relevant Diff Snippets
Dreamio/NativePlayerViewController.swift ยท center play/pause indicator contrast
343 unmodified lines34434534634734834935064 unmodified lines415416417418419420421422404 unmodified lines827828829830831832833834835836837838839343 unmodified linesaudioButton.showsMenuAsPrimaryAction = truecaptionsButton.showsMenuAsPrimaryAction = trueplayPauseButton.layer.cornerRadius = 28centerPlayPauseButton.layer.cornerRadius = 34centerPlayPauseButton.alpha = 0scrubber.addTarget(self, action: #selector(scrubbingStarted), for: .touchDown)scrubber.addTarget(self, action: #selector(scrubberChanged), for: .valueChanged)64 unmodified linescenterPlayPauseButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),centerPlayPauseButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),centerPlayPauseButton.widthAnchor.constraint(equalToConstant: 68),centerPlayPauseButton.heightAnchor.constraint(equalToConstant: 68),closeButton.widthAnchor.constraint(equalToConstant: 44),closeButton.heightAnchor.constraint(equalToConstant: 44),404 unmodified linesprivate func flashCenterPlayPauseIcon() {centerPlayPauseButton.setImage(UIImage(systemName: backend.isPlaying ? "pause.fill" : "play.fill"), for: .normal)guard !UIAccessibility.isReduceMotionEnabled else { return }centerPlayPauseButton.transform = CGAffineTransform(scaleX: 0.86, y: 0.86)UIView.animate(withDuration: 0.16, animations: {self.centerPlayPauseButton.alpha = 1self.centerPlayPauseButton.transform = .identity}) { _ inUIView.animate(withDuration: 0.22, delay: 0.28, options: [.curveEaseOut]) {self.centerPlayPauseButton.alpha = 0}}343 unmodified lines34434534634734834935035135235335435535635764 unmodified lines422423424425426427428429404 unmodified lines834835836837838839840841842843844845846847848849850851852853343 unmodified linesaudioButton.showsMenuAsPrimaryAction = truecaptionsButton.showsMenuAsPrimaryAction = trueplayPauseButton.layer.cornerRadius = 28centerPlayPauseButton.layer.cornerRadius = 40centerPlayPauseButton.backgroundColor = UIColor.black.withAlphaComponent(0.58)centerPlayPauseButton.layer.borderColor = UIColor.white.withAlphaComponent(0.38).cgColorcenterPlayPauseButton.layer.borderWidth = 1.5centerPlayPauseButton.layer.shadowColor = UIColor.black.cgColorcenterPlayPauseButton.layer.shadowOpacity = 0.45centerPlayPauseButton.layer.shadowRadius = 18centerPlayPauseButton.layer.shadowOffset = CGSize(width: 0, height: 8)centerPlayPauseButton.alpha = 0scrubber.addTarget(self, action: #selector(scrubbingStarted), for: .touchDown)scrubber.addTarget(self, action: #selector(scrubberChanged), for: .valueChanged)64 unmodified linescenterPlayPauseButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),centerPlayPauseButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),centerPlayPauseButton.widthAnchor.constraint(equalToConstant: 80),centerPlayPauseButton.heightAnchor.constraint(equalToConstant: 80),closeButton.widthAnchor.constraint(equalToConstant: 44),closeButton.heightAnchor.constraint(equalToConstant: 44),404 unmodified linesprivate func flashCenterPlayPauseIcon() {centerPlayPauseButton.setImage(UIImage(systemName: backend.isPlaying ? "pause.fill" : "play.fill"), for: .normal)if UIAccessibility.isReduceMotionEnabled {centerPlayPauseButton.transform = .identitycenterPlayPauseButton.alpha = 1UIView.animate(withDuration: 0.18, delay: 0.55, options: [.curveEaseOut]) {self.centerPlayPauseButton.alpha = 0}return}centerPlayPauseButton.transform = CGAffineTransform(scaleX: 0.82, y: 0.82)UIView.animate(withDuration: 0.16, animations: {self.centerPlayPauseButton.alpha = 1self.centerPlayPauseButton.transform = .identity}) { _ inUIView.animate(withDuration: 0.26, delay: 0.34, options: [.curveEaseOut]) {self.centerPlayPauseButton.alpha = 0}}
Diffs are generated with @pierre/diffs/ssr at documentation time. Each file diff stays inside its own shell so the page remains readable as a static HTML artifact.
Expected Impact for End-Users
Users should be able to quickly confirm play or pause state changes from the middle of the screen, even over bright scenes or high-motion content.
Validation
xcodebuild -workspace Dreamio.xcworkspace -scheme Dreamio -sdk iphonesimulator -configuration Debug buildโ succeeded.
Issues, Limitations, and Mitigations
- No known issues. This is a visual contrast adjustment scoped to the native player overlay.
New Changes as of 2026-05-26 22:22
Summary of changes
Softened the center play/pause flash from a black badge with a strong outline into a separate liquid-glass blur plate behind a clear icon button.
Why this change was made
The previous contrast fix made the transient center indicator visible, but overcorrected with a heavy black fill and outline. The revised treatment keeps the indicator readable while better matching the native Liquid Glass direction.
Code diffs
+ private let centerPlayPauseIndicator = NativePlayerViewController.centerGlassIndicator()
...
+ view.addSubview(centerPlayPauseIndicator)
view.addSubview(centerPlayPauseButton)
...
- centerPlayPauseButton.backgroundColor = UIColor.black.withAlphaComponent(0.58)
- centerPlayPauseButton.layer.borderColor = UIColor.white.withAlphaComponent(0.38).cgColor
- centerPlayPauseButton.layer.borderWidth = 1.5
+ centerPlayPauseButton.backgroundColor = .clear
+ centerPlayPauseButton.layer.borderWidth = 0
+ centerPlayPauseIndicator.alpha = 0
...
+ private static func centerGlassIndicator() -> UIVisualEffectView {
+ let view = glassPanel(cornerRadius: 34)
+ view.backgroundColor = UIColor.white.withAlphaComponent(0.08)
+ view.layer.borderColor = UIColor.white.withAlphaComponent(0.18).cgColor
+ view.layer.borderWidth = 0.75
+ view.isUserInteractionEnabled = false
+ return view
+ }
Related issues or PRs
No new Beads issue or PR was created for this small visual follow-up.
New Changes as of 2026-05-26 22:56
Summary of changes
Removed the positional/scale reveal motion from the native player controls. Tapping to show or hide controls now only fades the controls and close button in or out.
Why this change was made
The controls reveal animation felt overdone for a transient playback overlay. A simple opacity transition keeps the UI calmer while preserving clear visibility.
Code diffs
- let animations = {
- self.controlsContainer.alpha = 1
- self.closeButton.alpha = 1
- self.controlsContainer.transform = .identity
- }
- if UIAccessibility.isReduceMotionEnabled {
- animations()
- } else {
- controlsContainer.transform = CGAffineTransform(translationX: 0, y: 8).scaledBy(x: 0.98, y: 0.98)
- UIView.animate(withDuration: 0.22, delay: 0, options: [.curveEaseOut], animations: animations)
- }
+ controlsContainer.transform = .identity
+ UIView.animate(withDuration: 0.22, delay: 0, options: [.curveEaseOut]) {
+ self.controlsContainer.alpha = 1
+ self.closeButton.alpha = 1
+ }
Related issues or PRs
No new Beads issue or PR was created for this focused playback UI adjustment.
Follow-up Work
- None currently identified.