I learned the trick from Stefan Stanev's blog post on ListViewWebPart & SPView - two sides of the same coin.
In a nutshell, you make a shallow copy (a clone) of the relevant view using a schema that's built as you want it to appear, and then force the clone view to be updated. That, in turn, causes the existing view to be updated.
The key piece of code looks like this:
// load the desired schema into an XmlDocument
XmlDocument schemaDoc = new XmlDocument();
schemaDoc.LoadXml(desiredSchema.ToString());
// add the existing view's ID to the schema (via the Name attribute)
XmlElement root = doc.DocumentElement;
root.SetAttribute("Name", existingView.ID.ToString("B").ToUpper());
// create a clone view (to hold the desired schema)
SPView cloneView = new SPView(<relevant SPList>, schemaDoc);
// the magic nugget: by setting m_bExistsInDatabase=true, we force an update
typeof(SPView).
GetField("m_bExistsInDatabase",
BindingFlags.Instance | BindingFlags.NonPublic).SetValue(cloneView, true);
// causes the existing view to be updated with the desired schema.
cloneView.Update();
The schema is structured the same way as any view schema you can see in a Sharepoint connection in the Server Explorer window. Here's an example:
<View Type='HTML' Personal='FALSE' DisplayName='MyCustomViewEtc' DefaultView='FALSE'
Hidden='TRUE' Scope='FilesOnly' MobileView='FALSE' TabularView='FALSE'>
<ViewFields>
<FieldRef Name="Title" />
<FieldRef Name="OtherField" Explicit="TRUE" />
</ViewFields>
<Query>
<OrderBy><FieldRef Name="Title" /><OrderBy>
</Query>
<RowLimit Paged='TRUE'>100</RowLimit>
<Toolbar Type='None' />
</View>
Doing this requires considerable care because (as you can read in Stefan's blog post) when you create an XsltListViewWebPart, SharePoint creates a hidden companion SPView to go along with it - unless you provide the view yourself. Whether you create a view yourself or allow Sharepoint to create one for you will depend on the exact circumstances of your app.