41

The beamer command \alt<*overlay specification*>{foo}{bar} will insert foo if the current slide is within the given overlay specification, and bar otherwise. It's equivalent to

\only<*overlay specification*>{foo}\only<*complementary overlay specification*>{bar}

Here is a sample document using it:

\documentclass{beamer}
\begin{document}
\begin{frame}
\begin{align*}
y &= \frac{(x^2+1)\sqrt{x+3}}{x-1} \\
\ln y &= \ln (x^2+1) + \frac{1}{2} \ln(x+3) - \ln(x-1) \\
\frac{1}{y} \frac{dy}{dx}
      &= \frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\end{align*}
So
\[
\begin{split}
\frac{dy}{dx} &= \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}
\end{split}
\]
\end{frame}
\end{document}

This is very useful and saves typing. But if one of the alternatives is larger than the other, it leads to jiggling slides.

What I would like is a overlay-specification-aware command \altvisible that would take the same amount of space on each slide. So it would have to insert a box as big as the bigger of the two, and then set the correct material. So the above document with \altvisible instead of \alt would not jiggle.

Any takers?

3
  • Take a look at Uncover a multiline equation with beamer. Do the answers to this question help you? Mar 18, 2011 at 12:07
  • 1
    Such things are normally done using overprint, but I only used it for whole lines/paragraphs, never for short material. See my answer at the question liked by Hendrik. Mar 18, 2011 at 13:02
  • 2
    @Hendrik, @Martin: Thanks for the pointers. overprint is relevant, but because my particular use case is within a math expression it's not flexible enough. overlayarea might work. Mar 18, 2011 at 13:27

5 Answers 5

30

Following up the discussion of diabonas answer, here my suggestion. The idea to use phantom boxes seems the way to go. Here the two alternatives are boxed so that they can be measured. The code could be improved to detect the mode (text, math, display math, etc.) by itself and avoid the re-boxing which happens in the phantom commands.

\documentclass{beamer}
\newcommand<>\Alt[2]{{%
    \sbox0{$\displaystyle #1$}%
    \sbox1{$\displaystyle #2$}%
    \alt#3%
        {\rlap{\usebox0}\vphantom{\usebox1}\hphantom{\ifnum\wd0>\wd1 \usebox0\else\usebox1\fi}}%
        {\rlap{\usebox1}\vphantom{\usebox0}\hphantom{\ifnum\wd0>\wd1 \usebox0\else\usebox1\fi}}%
}}

\begin{document}
\begin{frame}
\begin{align*}
y &= \frac{(x^2+1)\sqrt{x+3}}{x-1} \\
\ln y &= \ln (x^2+1) + \frac{1}{2} \ln(x+3) - \ln(x-1) \\
\frac{1}{y} \frac{dy}{dx}
      &= \frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\end{align*}
So
\[
\begin{split}
\frac{dy}{dx} &= \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}
\end{split}
\]
\end{frame}
\end{document}

Here the version which checks the mode and math-style automatically. It works in all math and text modes:

\documentclass{beamer}

\makeatletter
% Detect mode. mathpalette is used to detect the used math style
\newcommand<>\Alt[2]{%
    \begingroup
    \ifmmode
        \expandafter\mathpalette
        \expandafter\math@Alt
    \else
        \expandafter\make@Alt
    \fi
    {{#1}{#2}{#3}}%
    \endgroup
}

% Un-brace the second argument (required because \mathpalette reads the three arguments as one
\newcommand\math@Alt[2]{\math@@Alt{#1}#2}

% Set the two arguments in boxes. The math style is given by #1. \m@th sets \mathsurround to 0.
\newcommand\math@@Alt[3]{%
    \setbox\z@ \hbox{$\m@th #1{#2}$}%
    \setbox\@ne\hbox{$\m@th #1{#3}$}%
    \@Alt
}

% Un-brace the argument
\newcommand\make@Alt[1]{\make@@Alt#1}

% Set the two arguments into normal boxes
\newcommand\make@@Alt[2]{%
    \sbox\z@ {#1}%
    \sbox\@ne{#2}%
    \@Alt
}

% Place one of the two boxes using \rlap and place a \phantom box with the maximum of the two boxes
\newcommand\@Alt[1]{%
    \alt#1%
        {\rlap{\usebox0}}%
        {\rlap{\usebox1}}%
    \setbox\tw@\null
    \ht\tw@\ifnum\ht\z@>\ht\@ne\ht\z@\else\ht\@ne\fi
    \dp\tw@\ifnum\dp\z@>\dp\@ne\dp\z@\else\dp\@ne\fi
    \wd\tw@\ifnum\wd\z@>\wd\@ne\wd\z@\else\wd\@ne\fi
    \box\tw@
}

\makeatother

\begin{document}

% Test the different modes and math styles
\begin{frame}
Display:
\[
\begin{split}
\frac{dy}{dx} &= \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}.
\end{split}
\]

In-Text:
\(
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right)
\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}.
\)

Subscript:
\(
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}.
\)
\[
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}.
\]

Sub-Subscript:
\(
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}}.
\)
\[
\frac{dy}{dx} = \left(\frac{2x}{x^2+1} + \frac{1}{2(x+3)} - \frac{1}{x-1}
\right) X_{X_{\Alt<2>{\frac{(x^2+1)\sqrt{x+3}}{x-1}}{y}}}.
\]

Text-mode:
XXXX  \Alt<2>{aaaaa}{Ag}.


\end{frame}
\end{document}

The dots at the end are to visualize the constant width only.

Result

11
  • 1
    This looks excellent. I will test it on Monday. Mar 19, 2011 at 12:33
  • @Matthew: Did this work out for you? May 10, 2011 at 13:36
  • 2
    I find myself using this again and thinking that it deserves more than a paltry three votes ... Dec 2, 2011 at 11:30
  • 2
    @AndrewStacey: I have to make a real package out of it and announce it here. Maybe then I get some more votes. Dec 2, 2011 at 11:43
  • 4
    Do that! I've already put it in a .sty file in my own texmf tree for easy use in various documents. I called it extalt, most likely for extended alt (I chose the name back in May so the actual reasons are - thankfully - lost in the mists (and midsts) of time). Dec 2, 2011 at 11:50
22

The second \Alt command in Martin Scharrer's answer is excellent; however, it can cause problems when used with Beamer's incremental overlay specifications (e.g., <+>). This is because Martin's implementation invokes the underlying \alt command using \mathpalette. \mathpalette internally uses \mathchoice, which actually typesets the contents given for each of the four math styles before deciding which to use in the final output.

Example of the issue

\documentclass{beamer}

% [Paste Martin's second set of `\Alt` macros here.]

\begin{document}

\begin{frame}
  \begin{itemize}
    \item Some stuff\dots

      \pause

    \item An equation I'll reveal in pieces:
      \[
        \frac{-4}{2} + \frac{9}{3} =
        \Alt<+>{
          \vcenter{\hbox{ ?\thinspace?}}
        }{
          \frac{-4 + 9}{2 + 3} = 1.
        }
      \]

      \pause

    \item Some more stuff\dots
  \end{itemize}
\end{frame}

\end{document}

Compiling this code yields a document with seven slides rather than the desired four, as TeX is executing the Beamer \alt macro---and thus incrementing the beamerpauses counter---four times rather than one.

Solving the problem

Move the \alt invocation outside of \mathpalette/\mathchoice. Unfortunately, this means that the new \Alt command's arguments will be typeset eight times (four for each of the \alt macro's two arguments) rather than four times as in Martin's code, but the added overhead seems necessary to make \Alt behave the same as \alt with respect to incremental overlays.

Also, it's a minor thing, but adding a \leavevmode before typesetting the \Alt content box seems to make the command behave a bit less surprisingly in some cases, e.g., at the start of a list item.

Revised code (supports incremental overlays)

\usepackage{etoolbox} % For `\ifbool`, `\ifnumcomp`.

\makeatletter
\newcommand*\Alt{\alt{\Alt@branch0}{\Alt@branch1}}

\newcommand\Alt@branch[3]{%
  \begingroup
  \ifbool{mmode}{%
    \mathchoice{%
      \Alt@math#1{\displaystyle}{#2}{#3}%
    }{%
      \Alt@math#1{\textstyle}{#2}{#3}%
    }{%
      \Alt@math#1{\scriptstyle}{#2}{#3}%
    }{%
      \Alt@math#1{\scriptscriptstyle}{#2}{#3}%
    }%
  }{%
    \sbox0{#2}%
    \sbox1{#3}%
    \Alt@typeset#1%
  }%
  \endgroup
}

\newcommand\Alt@math[4]{%
  \sbox0{$#2{#3}\m@th$}%
  \sbox1{$#2{#4}\m@th$}%
  \Alt@typeset#1%
}

\newcommand\Alt@typeset[1]{%
  \ifnumcomp{\wd0}{>}{\wd1}{%
    \def\setwider   ##1##2{##2##1##2 0}%
    \def\setnarrower##1##2{##2##1##2 1}%
  }{%
    \def\setwider   ##1##2{##2##1##2 1}%
    \def\setnarrower##1##2{##2##1##2 0}%
  }%
  \sbox2{}%
  \sbox3{}%
  \setwider2{\wd}%
  \setwider2{\ht}%
  \setwider2{\dp}%
  \setnarrower3{\ht}%
  \setnarrower3{\dp}%
  \leavevmode
  \rlap{\usebox#1}%
  \usebox2%
  \usebox3%
}
\makeatother
2
  • 3
    +1 for improving an already good answer a year later and the excellent explanations!
    – Daniel
    Mar 31, 2012 at 22:05
  • Thanks! Your version worked for me whereas neither of the ones by @MartinScharrer did.
    – a3nm
    Aug 26, 2018 at 15:31
12

As a further revision to Martin Scharrer's and bcat's nifty answers, here is a version that allows one to specify the position (l, c, or r) of the smaller text within the larger box:

\usepackage{etoolbox}
\usepackage{mathtools}

\makeatletter
% Detect mode. mathpalette is used to detect the used math style
\newcommand<>\Alt[3][l]{%
  \begingroup
    \providetoggle{Alt@before}%
    \alt#4{\toggletrue{Alt@before}}{\togglefalse{Alt@before}}%
    \ifbool{mmode}{%
      \expandafter\mathpalette
      \expandafter\math@Alt
    }{%
      \expandafter\make@Alt
    }%
    {{#1}{#2}{#3}}%
  \endgroup
}

% Un-brace the second argument (required because \mathpalette reads the three arguments as one
\newcommand\math@Alt[2]{\math@@Alt{#1}#2}

% Set the two arguments in boxes. The math style is given by #1. \m@th sets \mathsurround to 0.
\newcommand\math@@Alt[4]{%
  \setbox\z@ \hbox{$\m@th #1{#3}$}%
  \setbox\@ne\hbox{$\m@th #1{#4}$}%
  \@Alt{#2}%
}

% Un-brace the argument
\newcommand\make@Alt[1]{\make@@Alt#1}

% Set the two arguments into normal boxes
\newcommand\make@@Alt[3]{%
  \sbox\z@ {#2}%
  \sbox\@ne{#3}%
  \@Alt{#1}%
}

% Place one of the two boxes using \rlap and place a \phantom box with the maximum of the two boxes
\newcommand\@Alt[1]{%
  \setbox\tw@\null
  \ht\tw@\ifnum\ht\z@>\ht\@ne\ht\z@\else\ht\@ne\fi
  \dp\tw@\ifnum\dp\z@>\dp\@ne\dp\z@\else\dp\@ne\fi
  \wd\tw@\ifnum\wd\z@>\wd\@ne\dimexpr\wd\z@/2\relax\else\dimexpr\wd\@ne/2\relax\fi
  %
  \ifstrequal{#1}{l}{%
    \rlap{\iftoggle{Alt@before}{\usebox\z@}{\usebox\@ne}}%
    \copy\tw@
    \box\tw@
  }{%
    \ifstrequal{#1}{c}{%
      \copy\tw@
      \clap{\iftoggle{Alt@before}{\usebox\z@}{\usebox\@ne}}%
      \box\tw@
    }{%
      \ifstrequal{#1}{r}{%
        \copy\tw@
        \box\tw@
        \llap{\iftoggle{Alt@before}{\usebox\z@}{\usebox\@ne}}%
      }{%
      }%
    }%
  }%
}
\makeatother

Unlike bcat's macro, I use \mathpalette and avoid the bug in Martin's answer by toggling a flag inside \alt; the flag says whether the first or second argument of \Alt should be set.

This was my first attempt at writing a nontrivial macro involving plain TeX, so please let me know if there are better style practices I should use.

4
  • Scratch registers with an odd index (\box1, for instance) should be used only for global assignments. This is a recommended practice, not following it might lead to memory problems.
    – egreg
    Jul 16, 2012 at 22:28
  • @egreg So I should not be using \box\@ne in this case? Should I prefer the LaTeX style of \newsavebox etc. here, or is there a reason to stick to the plain TeX primitives for this macro? Thanks in advance for your help! Jul 17, 2012 at 17:06
  • You can use \box\tw@ or \box4, \box6, \box8; in this case the scratch boxes seem to be correctly used. When in doubt allocate new ones.
    – egreg
    Jul 17, 2012 at 18:27
  • 2
    Could you consider giving an example of how your new macro with the additional parameter is supposed to be used?
    – a3nm
    Aug 26, 2018 at 15:26
4

Maybe \vphantom could help? The following code defines a macro \altvphantom which uses the normal \alt command and additionally inserts \vphantom{<first argument>} and \vphantom{<second argument>} to ensure a consistent vertical spacing and to prevent the jiggling:

\newcommand<>{\altvphantom}[2]{\alt#3{#1}{#2}\vphantom{#1}\vphantom{#2}}

Just add this line to your document and replace \alt<2>{...}{...} with \altvphantom{...}{...}, and the preceding lines will stop moving around.

UPDATE: If you do not want the last line to move left and right either, you can say \usepackage{mathtools} in the preamble and change the "\alt-line" to

\altvphantom<2>{\mathrlap{\frac{(x^2+1)\sqrt{x+3}}{x-1}}}{\mathrlap{y}}

Another possibilty would be to use the fleqn option: If you use \documentclass[fleqn]{beamer}, all the equations will flush left, which solves the problem with the moving line, too.

8
  • This doesn't appear to work. Did you test it?
    – Seamus
    Mar 18, 2011 at 14:49
  • @Seamus Yes, I tested it and it worked fine for me. I uploaded the resulting PDF for you.
    – diabonas
    Mar 18, 2011 at 14:53
  • @Seamus @diabonas thanks. The vertical jiggling is gone (because the \vphantom{#1}{#2} stretches the line to have a height equal to the maximum of the two) but it still moves horizontally. Mar 18, 2011 at 15:13
  • @Matthew @Seamus I've updated my solution to prevent this movement, too.
    – diabonas
    Mar 18, 2011 at 15:24
  • 1
    @Matthew: You could place the shorter one in a \rlap macro and use \phantom{<longer one>} to fill the correct amount of space. Mar 18, 2011 at 15:26
1

Here is the definition of alt in beamerbaseoverlay.sty:

\def\alt{\@ifnextchar<{\beamer@alt}{\beamer@alttwo}}
\long\def\beamer@alttwo#1#2{\beamer@ifnextcharospec{\beamer@altget{#1}{#2}}{#1}}
\long\def\beamer@altget#1#2<#3>{%
  \def\beamer@doifnotinframe{#2}\def\beamer@doifinframe{#1}%
  {\beamer@masterdecode{#3}}\beamer@donow}
\long\def\beamer@alt<#1>#2#3{%
  \def\beamer@doifnotinframe{#3}\def\beamer@doifinframe{#2}%
  {\beamer@masterdecode{#1}}\beamer@donow}

Weirdly, \uncover is defined in terms of \alt:

\newcommand{\uncover}{\alt{\beamer@fakeinvisible}{\beamer@makecovered}}

So I suspect that putting \beamer@fakeinvisible and \beamer@makeuncovered at some point in a definition like that of \alt should work? But I couldn't work out where exactly to fit it in.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .