The below should loop your post types and only insert the header once if it has a post for that post type. I am sure there is a lot better ways to code this cleaner but this should work * I have not tested only coded in this page.
I am not sure about the ordering of the custom post type attribute as I do not know that plugin and your query looks to be correct might be worth trying without the _ as I have done this in Advanced Custom Fields and did not require the _ .
would be worth checking your database as I know advanced custom fields saves dates like this 03212017 and WordPress might not be able to see this as a valid date.
if (have_posts()) :
$counter = 1;
$post_counter_1 = 0;
$post_counter_2 = 0;
$post_counter_3 = 0;
while ( have_posts() ) : the_post();
// Get post type to display
if(get_post_type() == 'post'){
// run once for creating header
if($post_counter_1 == 0)
{
$post_counter_1 = 1;
echo '<h4><i class="fa fa-newspaper-o"></i> Articles</h4>';
}
echo '<div class="results post">';
get_template_part('parts/search', 'post');
}
endwhile;
while ( have_posts() ) : the_post();
if(get_post_type() == 'tribe_events'){
if($post_counter_2 == 0)
{
$post_counter_2 = 1;
echo '<h4><i class="fa fa-truck"></i> Distributors Wanted</h4>';
}
echo '<div class="results tribe_events">';
get_template_part('parts/search', 'tribe_events');
}
endwhile;
while ( have_posts() ) : the_post();
if(get_post_type() == 'distribute'){
if($post_counter_3 == 0)
{
$post_counter_3 = 1;
echo '<h4 class="events">Events</h4>';
}
echo '<div class="results distribute">';
get_template_part('parts/search', 'distribute');
}
endwhile;
endif;