Custom Controls on iOS
See original GitHub issueI am using usePlyr to create a custom plyr instance but on mobile the audio controls don’t work. The icon shows as unmuted and doesn’t unmute the audio when tapped. Has anyone else had this issue?
import React, { useState, useContext } from "react";
import type { APITypes } from "plyr-react";
import { usePlyr, PlyrProps, PlyrInstance } from "plyr-react";
import styles from "./Play.module.scss";
import { CoreContext } from "~context";
import hexToRgb from "~shared/utils/hexToRgb";
import { useGTM } from "~hooks/useGTM";
const CustomPlyr = React.forwardRef<APITypes, PlyrProps>((props, ref: any) => {
const { playerStyle, size }: any = useContext(CoreContext);
const { track } = useGTM();
const { source, options = null } = props;
const raptorRef = usePlyr(ref, { options, source });
const { poster } = source || {};
const { active } = options?.loop || {};
const { autoplay } = options || {};
const { gradient, subtitles, ui, full } = playerStyle;
const [current, setCurrent] = useState(0);
const [progress, setProgress] = useState(0);
const [playing, setPlaying] = useState(autoplay);
const [muted, setMuted] = useState(true);
const [showLoopScreen, setShowLoop] = useState(false);
const [counter, setCounter] = useState<number>(0);
React.useEffect(() => {
if (ref?.current?.plyr?.source === null) return;
const api = ref?.current as { plyr: PlyrInstance };
setMuted(api?.plyr?.volume === 0);
const canPlay = () => {
if (autoplay && !playing) {
api?.plyr?.play();
}
};
const onPlay = () => {
setShowLoop(false);
setPlaying(true);
};
const onPause = () => {
setPlaying(false);
};
const onEnded = () => {
setShowLoop(!active);
setCounter(0);
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `video-completed`,
event: "coreDataPush",
});
};
const onUpdate = () => {
setCurrent(Math.round(api?.plyr?.currentTime));
if (api?.plyr?.paused === false) {
if (current > 0.5) {
setProgress(
Number(
(api?.plyr?.currentTime / api?.plyr?.duration) * 100
)
);
if (progress > 1 && counter === 0) {
setCounter(1);
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `video-start`,
event: "coreDataPush",
});
}
if (progress > 25 && counter === 1) {
setCounter(2);
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `'video-played-25-percent`,
event: "coreDataPush",
});
}
if (progress > 50 && counter === 2) {
setCounter(3);
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `'video-played-50-percent`,
event: "coreDataPush",
});
}
if (progress > 75 && counter === 3) {
setCounter(4);
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `'video-played-75-percent`,
event: "coreDataPush",
});
}
if (progress < 5 && counter === 4) {
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `video-completed`,
event: "coreDataPush",
});
setShowLoop(!active);
track({
category: "adops",
adOpsAction: "video",
adOpsLabel: `video-start`,
event: "coreDataPush",
});
setCounter(1);
}
}
}
};
api?.plyr?.on("canplay", canPlay);
api?.plyr?.on("playing", onPlay);
api?.plyr?.on("pause", onPause);
api?.plyr?.on("timeupdate", onUpdate);
api?.plyr?.on("ended", onEnded);
const offFuncs = () => {
api?.plyr?.off("canplay", canPlay);
api?.plyr?.off("playing", onPlay);
api?.plyr?.off("pause", onPause);
api?.plyr?.off("timeupdate", onUpdate);
api?.plyr?.off("ended", onEnded);
};
if (ref?.current?.plyr?.source) {
return offFuncs;
}
});
if (!source || !ref) {
return null;
}
return (
<div
data-size={size}
data-full={full}
className={styles.playercontainer}
>
{showLoopScreen || (!autoplay && current < 1 && !playing) ? (
<div
className={styles.loopscreen}
style={{
backgroundImage: `url(${poster})`,
}}
>
{!autoplay && current < 1 && !playing ? (
<svg
viewBox="0 0 503 612"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill={ui?.color}
d="M0 608.7l503.2-302.6L0 3.5z"
/>
</svg>
) : (
<svg viewBox="0 0 425.47 402.84">
<g>
<path
className="st0"
fill="white"
d="M138.18 396.9c-10.19-7.86-20.46-15.61-30.56-23.6-16.19-12.81-32.27-25.76-48.38-38.66-7.39-5.92-7.53-12.7-.3-18.51 24.41-19.58 48.87-39.09 73.26-58.69 4.09-3.28 8.27-5.07 13.27-2.24 3.92 2.22 5.33 5.76 5.31 10.13-.06 12.89-.02 25.78-.02 39.35h4.97c46.14 0 92.29.11 138.43-.04 41.76-.13 74.9-27.79 82.55-68.79 2.98-15.96.89-31.48-5.42-46.39-2.3-5.42-3.1-10.8-1.14-16.44 2.78-8.01 10.02-13.41 18.62-13.9 8.19-.47 16.06 4.3 19.68 12.32 7.4 16.4 10.93 33.63 10.72 51.64-.77 67.34-53.92 121.52-121.27 122.55-46.94.71-93.9.18-140.85.2h-6.3v22.05c0 4.84-.33 9.71.08 14.51.56 6.71-1.71 11.56-7.82 14.51h-4.83zM274.71 98.24c-2.04-.09-3.61-.23-5.18-.23-45.98-.01-91.96-.09-137.95.02-42 .1-75.29 27.77-82.89 68.98-2.94 15.95-.82 31.48 5.54 46.38 2.31 5.42 3 10.82.98 16.44-2.88 8.02-10.17 13.36-18.73 13.73-8.26.36-15.93-4.44-19.56-12.51-7.38-16.42-10.87-33.65-10.64-51.66.87-67.32 54.04-121.34 121.46-122.34 46.94-.7 93.9-.17 140.85-.19h6.12V25.92c0-2.42.11-4.85-.02-7.26-.27-5.08 1.12-9.31 5.98-11.67 4.95-2.4 8.95-.32 12.83 2.8 13.05 10.53 26.19 20.97 39.29 31.44 10.83 8.66 21.67 17.31 32.5 25.98 8.82 7.06 8.8 13.35-.03 20.4-23.56 18.83-47.11 37.68-70.66 56.52-1.01.81-2.02 1.6-3.05 2.38-3.54 2.68-7.34 3.23-11.31 1.1-3.75-2.01-5.52-5.26-5.52-9.51 0-11.45 0-22.91-.01-34.36v-5.5z"
/>
</g>
</svg>
)}
</div>
) : (
<div
style={{
backgroundColor:
hexToRgb(gradient?.color, 0.75) ||
"rgba(0,0,0, .75)",
}}
className={styles.overlay}
>
<div
style={{ backgroundColor: size === "one" && ui?.color }}
className={styles.circle}
>
{current < 60 && (
<span
style={{
color:
size === "one"
? gradient?.color
: ui?.color,
}}
>
{current}
</span>
)}
<svg fill="rgba(0,0,0,0)" className={styles.progress}>
<circle
cx="25"
cy="25"
r="22"
style={{
backgroundColor: "none",
stroke:
hexToRgb(`${ui?.color}`, 0.4) ||
"white",
}}
className={styles.track}
/>
<circle
cx="25"
cy="25"
r="22"
className={styles.pct}
style={{
backgroundColor: "none",
stroke: ui?.color,
strokeDashoffset: `${
(1 - progress / 200) *
(2 * (22 / 7) * 40)
}`,
}}
/>
</svg>
</div>
<button
onClick={() => {
ref.current.plyr.togglePlay();
}}
className={styles.action}
>
{playing ? (
<svg
viewBox="0 0 503 594"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill={ui?.color}
d="M2.2 2.2h174.2V594H2.2zM328.8 2.2H503V594H328.8z"
/>
</svg>
) : (
<svg
viewBox="0 0 503 612"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill={ui?.color}
d="M0 608.7l503.2-302.6L0 3.5z"
/>
</svg>
)}
</button>
<button
onClick={() => {
if (muted) {
ref.current.plyr.increaseVolume(1);
} else {
ref.current.plyr.decreaseVolume(1);
}
}}
className={styles.volume}
>
{muted ? (
<svg viewBox="0 0 510.75 511.76">
<path
fill={ui?.color}
d="M474.37 511.76h-1c-1.15-1.37-2.2-2.85-3.46-4.11-15.75-15.76-31.56-31.47-47.27-47.27-2.1-2.11-3.75-4.65-5.65-7.03-31.84 25.6-66.21 42.9-104.69 51.7-.32-.67-.59-.96-.59-1.25-.05-18.13-.14-36.25.05-54.37.01-1.32 1.92-3.19 3.38-3.83 9.26-4.07 18.98-7.21 27.89-11.92 11.17-5.91 21.7-13.02 32.04-19.33L254.62 293.9v188.32c-.42.19-.83.37-1.25.56-1.15-1.43-2.18-2.97-3.47-4.26-44.54-44.59-89.13-89.13-133.63-133.76-2.41-2.42-4.79-3.48-8.25-3.47-33.26.13-66.52.08-99.78.06-2.99 0-5.97-.16-8.96-.25V171.43c1.99-.09 3.97-.25 5.96-.25 40.56-.01 81.13-.01 121.69-.01h6.19c-1.83-2.01-2.78-3.12-3.81-4.15C87.13 124.8 44.93 82.6 2.71 40.43 1.68 39.41.44 38.6-.69 37.69v-1C11.03 24.46 22.75 12.24 34.3.19c159.26 159.26 317.32 317.32 476.51 476.5-12.04 11.58-24.24 23.32-36.44 35.07zM311.62 7.81c162.83 35.77 247.51 220.81 169.16 366.17-13.68-13.73-27.33-27.31-40.74-41.12-1.02-1.05-.8-3.88-.34-5.68 3.86-15.42 8.84-30.62 11.7-46.21 4.68-25.57 2-51.06-4.76-76.02-12.4-45.82-38.19-82.54-76.6-110.33-16.25-11.76-33.98-20.82-53.16-26.78-4.35-1.35-5.45-3.36-5.37-7.69.28-15.63.11-31.26.11-46.89V7.81z"
/>
<path
fill={ui?.color}
d="M380.93 274.35c-6.71-6.8-12.73-12.96-18.82-19.06-15.84-15.88-31.74-31.7-47.51-47.65-1.38-1.39-2.83-3.53-2.85-5.33-.2-19.76-.13-39.53-.13-59.39 41.15 17.18 78.7 71.35 69.31 131.43zM196.95 88c18.71-18.67 38.08-37.99 57.47-57.33v114.81c-18.81-18.81-38.11-38.12-57.47-57.48z"
/>
</svg>
) : (
<svg viewBox="0 0 510.75 511.76">
<path
fill={ui?.color}
d="M0 170.93c2.98-.09 5.97-.25 8.95-.25 33.25-.02 66.51-.06 99.76.06 3.45.01 5.84-1.04 8.25-3.46 44.49-44.62 89.07-89.16 133.61-133.73 1.29-1.29 2.34-2.82 4.47-3.71v452.25c-1.55-1.43-2.87-2.57-4.1-3.8-44.57-44.55-89.15-89.08-133.63-133.71-2.67-2.67-5.3-3.83-9.1-3.82-34.08.14-68.17.09-102.25.07-1.99 0-3.98-.16-5.97-.25C0 284.04 0 227.48 0 170.93zM312.32 504.13v-35.44c0-5.99.24-11.99-.09-17.96-.2-3.65 1.11-5.22 4.53-6.29 21.61-6.77 41.38-17.18 59.28-31.04 39-30.19 64.03-69.36 73.94-117.68 17.18-83.77-20.14-167.68-94.04-211.03-12.36-7.25-26.17-12.05-39.45-17.67-3.04-1.29-4.31-2.52-4.27-6 .21-17.78.1-35.56.1-53.4 90.78 17.36 187.77 102.27 197.57 226.6 11.55 146.59-94.73 249.1-197.57 269.91z"
/>
<path
fill={ui?.color}
d="M312.63 142.19c36.6 16.84 71.16 59.71 70.6 114.87-.57 55.95-36.91 97.18-70.6 112.3V142.19z"
/>
</svg>
)}
</button>
</div>
)}
<video
ref={raptorRef as React.MutableRefObject<HTMLVideoElement>}
className="plyr-react plyr"
/>
</div>
);
});
CustomPlyr.displayName = "CustomPlyr";
export default CustomPlyr;
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:14 (1 by maintainers)
Top Results From Across the Web
Edit Control Center on your iPhone, iPad, and iPod touch
You can customize Control Center by adding controls for apps, settings, and features like Camera, Music Recognition, Dark Mode, and more.
Read more >iOS: How to customize Control Center on iPhone and iPad
Tap on Settings · Swipe down and tap Control Center · Tap the next to any item you'd like to add under “More...
Read more >Cocoa Controls: Custom Controls for iOS, iPadOS, macOS ...
6,674 open source UI components for iOS, iPadOS, macOS, tvOS, and watchOS. Stay in touch.
Read more >Custom Controls in iOS | Kodeco, the new raywenderlich.com
Custom controls are an important part of developing apps for iOS, allowing you to develop engaging user experiences, while achieving great code separability ......
Read more >How to customize and use Control Center on iPhone
How to customize Control Center on iPhone · Launch the Settings app on your iPhone. · Tap Control Centre. Tap on Control Centre...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@realamirhe Thanks again for looking into it, I’m not sure what the issue is but I and my teammates are still seeing the video as muted no matter how much we click the unmute button. I’m not sure if this helps but I’m on an iPhone 13 running iOS 15.6
I removed a lot of the dynamic aspects of the code and replaced it with hard coded values in order to make the test work but here is the sample code for you to review.
https://codepen.io/c-mella/pen/JjvPRjM
Thanks again for you help