beamer overlays and table rules

I am trying to use \only{...} within a table to selectively add blocks of lines. This works as long as I don't have a \midrule or \bottomrule included or following the part contained in \only{...}.

Why does this happen and is there a way to achieve this nevertheless?

An example follows:

\documentclass{beamer}
\usepackage{booktabs}

\begin{document}
\begin{frame}{Example}
\begin{tabular}{ll}
\toprule
bla & bla \\
\midrule
A & A \\
B & B \\
\only<1>{%
\midrule
C & C \\
D & D \\ }
\bottomrule
\end{tabular}

\only<2>{Something else}
\end{frame}

\end{document}


I managed to get it working with some carefully placed comments, see below for example. It seems that LaTeX doesn't like it when you place \\ and \*rule on different lines.

\documentclass{beamer}
\usepackage{booktabs}

\begin{document}
\begin{frame}{Example}
\begin{tabular}{ll}
\toprule
bla & bla \\
\midrule
A & A \\
B & B %
\only<1>{%
\\\midrule
C & C %
}%
\only<2>%
{%
\\
D & D  %
}%
\\ \bottomrule
\end{tabular}
\end{frame}

\end{document}

• Looks very strange, though... – user52366 Mar 22 '17 at 19:01
• LaTeX does not care whether \\  and \*rule are on the same line or not. The linebreak is nothing else than a space token. The problem is the \only in between. See my answer for more information. – jakun Jul 15 at 19:33

I'm not sure why this happens but here's a heavy-handed solution: write your tabular environment twice inside different \only statements.

\begin{frame}{Example}
\only<1>{\begin{tabular}{ll}
\toprule
bla & bla \\
\midrule
A & A \\
B & B \\
\midrule
C & C \\
D & D \\
\bottomrule
\end{tabular}
}
\only<2->{\begin{tabular}{ll}
\toprule
bla & bla \\
\midrule
A & A \\
B & B \\
\bottomrule
\end{tabular}
}
\only<2>{Something else}
\end{frame}


You are getting a ! Misplaced \noalign because, after full expansion, there may be no remaining tokens between the TeX primitives \cr, which is used in \\, and \noalign, which is used in the \*rule macros. Tokens that disappear during the expansion, like \if, are fine. But \only cannot be fully expandable because it needs to keep track of how many overlays there are on this slide. Therefore it cannot be used between \\ and \*rule. (Same goes for \multicolumn, by the way).

I would not use \only because it places nothing instead on those overlays where the argument is hidden, causing the content of the frame to jump around. Instead I would use \uncover, which behaves like \visible by default. This means that the argument is not visible on not matching overlays, but there is space reserved as much as there would be needed, if the argument was actually placed there. This avoids undesired jumping. If you want a different behaviour later on you can easily change it with \setbeamercovered, e.g. setting it to transparent. But of course, this changes nothing about the problem at hand.

\only and \uncover are both special cases of the \alt command which places the first argument on matching overlays and the second argument on not matching overlays. I have implemented a simplified, fully expandable version of \alt. Because it can not keep track of the overlays, the exising overlays must be declared separately—either at the beginning of the frame with \begin{frame}<-5> or by a usual overlayaware command like \only. I have used this \xalt to define an overlayaware version of the \*rule macros.

I have not reimplemented the full power of overlay specifications but only the most important ones:

• <n> a single overlay
• <n-m> a range of overlays
• <n-> all overlays ≥ n
• <-m> all overlays ≤ m
• <*> all overlays

The lookahead whether there is an optional overlay specification given after the \*rule macros is not quite as safe as usual, because it needs to be fully expandable. Therefore you should make sure that there is no { following one of my redefined \*rule macros. But I cannot think of any situation where it would make much sense to start a cell with a {, anyway.

I have taken the information how much space I need to insert instead of a \*rule macro from the booktabs documentation section 7 Technical summary of commands.

\documentclass{beamer}
\usepackage{booktabs}

% ------- \xalt -------

\makeatletter

\providecommand\@secondofthree[3]{#2}

\def\xalt<#1>{%
\if *#1%
% matches any overlay
\expandafter \@secondofthree
\else
% continue overlay check
\expandafter \@firstofone
\fi
{\@xalt@checkrange<#1->}%
}

\def\@xalt@checkrange<#1-#2>{%
\ifx \relax#2\relax
% is not a range
\expandafter \@firstoftwo
\else
% is a range
\expandafter \@secondoftwo
\fi
{\@xalt@checknumber<#1>}%
{\@xalt@checklower<#1-#2>}%
}

\def\@xalt@checknumber<#1>{%
\ifnum \beamer@slideinframe=#1
% overlay is matched
\expandafter \@firstoftwo
\else
% overlay is *not* matched
\expandafter \@secondoftwo
\fi
}

\def\@xalt@checklower<#1-#2>{%
\ifx \relax#1\relax
% lower bound not given => match
\expandafter \@firstofone
\else \ifnum \beamer@slideinframe<#1
% overlay is *not* matched
\expandafter\expandafter\expandafter \@thirdofthree
\else
% lower bound is matched
\expandafter\expandafter\expandafter \@firstofone
\fi \fi
{\@xalt@checkupper<-#2>}%
}

% #2 ends with an additional -

\def\@xalt@checkupper<-#1->{%
\ifx \relax#1\relax
% upper bound not given => match
\expandafter \@firstoftwo
\else \ifnum \beamer@slideinframe>#1
% overlay is *not* matched
\expandafter\expandafter\expandafter \@secondoftwo
\else
% upper bound is matched
\expandafter\expandafter\expandafter \@firstoftwo
\fi \fi
}

\makeatother

% ------- \toprule -------

\let\originaltoprule=\toprule

\def\overlayawaretoprule<#1>{%
\xalt<#1>%
{\toprule}%
{\addlinespace[\dimexpr \abovetopsep + \heavyrulewidth + \belowrulesep \relax]}%
}

\renewcommand{\toprule}[1]{%
\ifx <#1%
\expandafter \overlayawaretoprule
\else
\expandafter \originaltoprule
\fi
#1%
}

% ------- \midrule -------

\let\originalmidrule=\midrule

\def\overlayawaremidrule<#1>{%
\xalt<#1>%
{\midrule}%
{\addlinespace[\dimexpr \aboverulesep + \lightrulewidth + \belowrulesep \relax]}%
}

\renewcommand{\midrule}[1]{%
\ifx <#1%
\expandafter \overlayawaremidrule
\else
\expandafter \originalmidrule
\fi
#1%
}

% ------- \bottomrule -------

\let\originalbottomrule=\bottomrule

\def\overlayawarebottomrule<#1>{%
\xalt<#1>%
{\bottomrule}%
{\addlinespace[\dimexpr \aboverulesep + \heavyrulewidth + \belowbottomsep \relax]}%
}

\renewcommand{\bottomrule}[1]{%
\ifx <#1%
\expandafter \overlayawarebottomrule
\else
\expandafter \originalbottomrule
\fi
#1%
}

% ------- test document -------

\begin{document}

\begin{frame}{Example}
\begin{table}
\begin{tabular}{ll}
\toprule
bla & bla \\
\midrule
A & A \\
B & B \\
\midrule<2->
\uncover<2->{C & C} \\
\uncover<2->{D & D} \\
\bottomrule
\end{tabular}
\end{table}

\uncover<3->{Something else}
\end{frame}

\end{document}


If you want the behaviour of an \only you can simply leave the second argument of \xalt empty:

\def\xonly<#1>#2{\xalt<#1>{#2}{}}

\begin{document}

\begin{frame}{Example}
\begin{table}
\begin{tabular}{ll}
\toprule
bla & bla \\
\midrule
A & A \\
B & B \\
\xonly<2->{\midrule}
\xonly<2->{C & C \\}
\xonly<2->{D & D \\}
\bottomrule
\end{tabular}
\end{table}

\only<3->{Something else}
\end{frame}

\end{document}