The problem is not IFS 's contents, which is used as a field separator between the individual fields. Your read is missing the line terminator, usually <LF> (= \n = 0x0A = ^J) which is not printf ed. So read waits for it but runs into an EOF / EOT (as stdin terminates), discards the data read so far, and breaks out of the loop having unprocessed data in the variable(s) (Thanks to MadeInGermany's post#5 for mentioning that). You can
add a linefeed <LF> to the printf command
switch read to use a different line terminator (which still needs to be printf ed). man bash :
sea's proposal will set IFS to " ", "\", and "n", split input on any collection of those, and eliminate them from input. Set IFS like IFS=$' \n' to make it <space> and <line feed>.
Would you not be better re-writing the loop to have the loop read the data in rather than have it piped to? That might sound confusingly similar, but some shells will start a sub-process when you add a pipe into it. That might mean that the loop is actually in a sub-process and the scoping of variables may not be what you expect. Of course it might just be that your printf statement doesn't have a new-line after the last item.
Could you consider something like the following instead:-
while read variable
do
echo "I got variable ${variable}"
done < <(printf "item1\nitem2\nthird item\last\n")
This runs the process to generate the input in a subshell and your loop runs in the current process so that values might be more what you expect.