Custom-->TableView_Swizzle

本文介绍如何在Swift中通过自定义初始化方法为UITableView和UITableViewCell设置样式,包括去除分割线和选择高亮,使用Objective-C代码示例进行说明。

#import "UITableView+Swizzle.h"

#import <UIKit/UIKit.h>

#import <objc/objc.h>

#import <objc/runtime.h>

 

@implementation UITableView (Swizzle)

 

实现:一次性设置tableView的位置

+ (void)load

{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        [self swizzleInstanceMethod:@selector(initWithFrame:style:) withNewMethod:@selector(yq_initWithFrame:style:)];// 在这里替换

    });

}

 

- (instancetype)yq_initWithFrame:(CGRect)frame style:(UITableViewStyle)style

{

    UITableView *tableView = [self yq_initWithFrame:frame style:style];

    [tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];

    return tableView;

}

 

/**

 *  替换实例方法(对象方法)

 *

 *  @param originalSel 原方法SEL

 *  @param newSel      新方法SEL

 */

+ (BOOL)swizzleInstanceMethod:(SEL)originalSel withNewMethod:(SEL)newSel

{

    Method originalMethod = class_getInstanceMethod(self, originalSel);

    Method newMethod = class_getInstanceMethod(self, newSel);

 

    if (!originalMethod || !newMethod) return NO;

 

    class_addMethod(self, originalSel,  class_getMethodImplementation(self, originalSel), method_getTypeEncoding(originalMethod));

    class_addMethod(self, newSel, class_getMethodImplementation(self, newSel),  method_getTypeEncoding(newMethod));

    method_exchangeImplementations(class_getInstanceMethod(self, originalSel), class_getInstanceMethod(self, newSel));

    return YES;

}

 

TableViewCell_Swizzle 

#import "UITableViewCell+Swizzle.h"

 

@implementation UITableViewCell (Swizzle)

 

+ (void)load

{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        [self swizzleClassMethod:@selector(initWithStyle:reuseIdentifier:) withNewMethod:@selector(yq_initWithStyle:reuseIdentifier:)];

    });

}

 

- (instancetype)yq_initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

{

    UITableViewCell *cell = [self yq_initWithStyle:style reuseIdentifier:reuseIdentifier];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];

    return cell;

}

 

@end

 

转载于:https://www.cnblogs.com/happyEveryData/p/5546757.html

程序对选中行执行删除后程序卡死 #include "kchangemanage.h" #include "ui_kchangemanage.h" #include <QSqlTableModel> // 包含QSqlTableModel头文件 #include <QMessageBox> #include <QItemSelectionModel> #include "module/model/EquipmentDataModel.h" #include "module/model/filterproxymdel.h" using namespace view::equipment; kchangemanage::kchangemanage(QWidget *parent) : QDialog(parent), ui(new Ui::kchangemanage) { ui->setupUi(this); //初始化模型 dataModel::EquipmentDataModel *modelRent = new dataModel::EquipmentDataModel(this); fliterModel::FilterProxyMdel *rentFilterProxyMdel = new fliterModel::FilterProxyMdel(this); rentFilterProxyMdel->setSourceModel(modelRent); //给视图指定模型 ui->tableView_change->setModel(rentFilterProxyMdel); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_ID); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_Count); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_RentId); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_ClassId); // 调整列宽以显示数据 ui->tableView_change->resizeColumnsToContents(); ui->tableView_change->horizontalHeader()->setStretchLastSection(true); // 加载数据 modelRent->fetchData(); // 调试信息 qDebug() << "Model row count:" << modelRent->rowCount(); qDebug() << "Model column count:" << modelRent->columnCount(); } kchangemanage::~kchangemanage() { delete ui; } void kchangemanage::on_deleteButton_clicked() { QItemSelectionModel *selectionModel = ui->tableView_change->selectionModel(); if (!selectionModel->hasSelection()) { QMessageBox::warning(this, "警告", "请选择要删除的行!"); return; } // 获取选中的行(代理模型索引) QModelIndexList selected = selectionModel->selectedRows(); QModelIndex proxyIndex = selected.first(); // 映射到源模型索引 QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex); if (!sourceIndex.isValid()) { QMessageBox::critical(this, "错误", "无效的模型索引"); return; } // 获取主键ID int id = m_sourceModel->data( m_sourceModel->index(sourceIndex.row(), dataModel::EquipmentDataModel::Col_ID) ).toInt(); // 确认删除对话框 QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "确认删除", "确定要删除选中的记录吗?", QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { // 直接执行SQL删除 QSqlQuery query; query.prepare("DELETE FROM equipment WHERE id = :id"); query.bindValue(":id", id); if (query.exec()) { QMessageBox::information(this, "成功", "删除成功!"); // 从内存中移除数据 m_sourceModel->removeRow(sourceIndex.row()); // 通知视图更新 m_proxyModel->invalidate(); } else { QMessageBox::critical(this, "错误", "删除失败: " + query.lastError().text()); } } } void kchangemanage::on_cancelButton_clicked() { this->close(); }
07-13
<think>我们注意到在构造函数中,模型被声明为局部变量(modelRent和rentFilterProxyMdel),但在删除函数中却使用了成员变量m_sourceModel和m_proxyModel(这些成员变量在提供的代码中并没有被初始化)。 这会导致在删除函数中访问了未初始化的指针,从而引起程序崩溃或卡死。 解决方案: 1. 在类的头文件中声明这些模型为成员变量。 2. 在构造函数中初始化这些成员变量,而不是使用局部变量。 3. 确保在删除函数中使用的是成员变量。 此外,我们还需要注意: - 在删除操作中,我们获取了选中的行,然后映射到源模型,这是正确的。 - 但是,在删除数据库记录后,我们尝试从源模型中移除行。这里有一个问题:直接执行SQL删除并不会通知模型,所以模型并不知道数据已经被删除。虽然我们调用了`removeRow`,但这个方法可能不会正确工作,因为底层的数据(数据库)已经变了,而模型可能还在尝试操作已经被删除的数据。 建议的修改方式: 方法一:使用模型的方法来删除行(如果模型支持直接删除,比如QSqlTableModel有removeRow方法,然后调用submitAll提交)。但这里使用的是自定义模型EquipmentDataModel,我们需要看它是否支持removeRow。 方法二:在直接执行SQL删除后,重新加载模型数据(调用fetchData)以确保模型和数据库同步。但是注意,这样会丢失当前的选择和视图状态(如滚动位置)。 根据提供的代码,我们更倾向于将模型作为成员变量,并且在删除后重新加载数据(简单可靠)。同时,在删除操作中,我们使用成员变量来访问模型。 具体修改步骤: 1. 在头文件kchangemanage.h中声明成员变量: ```cpp private: Ui::kchangemanage *ui; dataModel::EquipmentDataModel *m_sourceModel; // 添加 fliterModel::FilterProxyMdel *m_proxyModel; // 添加 ``` 2. 修改构造函数,将局部变量赋值给成员变量: ```cpp kchangemanage::kchangemanage(QWidget *parent) : QDialog(parent), ui(new Ui::kchangemanage) { ui->setupUi(this); //初始化模型 m_sourceModel = new dataModel::EquipmentDataModel(this); // 修改 m_proxyModel = new fliterModel::FilterProxyMdel(this); // 修改 m_proxyModel->setSourceModel(m_sourceModel); // 修改 //给视图指定模型 ui->tableView_change->setModel(m_proxyModel); // 已经是m_proxyModel // ... 其他代码不变 // 加载数据 m_sourceModel->fetchData(); // 修改 // 调试信息 qDebug() << "Model row count:" << m_sourceModel->rowCount(); // 修改 // ... } ``` 3. 修改删除函数,使用成员变量,并且在删除后重新加载数据: ```cpp void kchangemanage::on_deleteButton_clicked() { QItemSelectionModel *selectionModel = ui->tableView_change->selectionModel(); if (!selectionModel->hasSelection()) { QMessageBox::warning(this, "警告", "请选择要删除的行!"); return; } // 获取选中的行(代理模型索引) QModelIndexList selected = selectionModel->selectedRows(); QModelIndex proxyIndex = selected.first(); // 映射到源模型索引 QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex); if (!sourceIndex.isValid()) { QMessageBox::critical(this, "错误", "无效的模型索引"); return; } // 获取主键ID int id = m_sourceModel->data( m_sourceModel->index(sourceIndex.row(), dataModel::EquipmentDataModel::Col_ID) ).toInt(); // 确认删除对话框 QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "确认删除", "确定要删除选中的记录吗?", QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { QSqlQuery query; query.prepare("DELETE FROM equipment WHERE id = :id"); query.bindValue(":id", id); if (query.exec()) { QMessageBox::information(this, "成功", "删除成功!"); // 由于直接删除了数据库中的记录,我们需要重新加载模型以更新视图 m_sourceModel->fetchData(); // 注意:不要调用 m_sourceModel->removeRow(sourceIndex.row()); // 因为我们已经重新加载了整个模型,而且removeRow可能会在模型内部引起不一致,因为数据库已经删除了该行。 // 通知代理模型更新(重新加载后其实不需要,因为fetchData会重置模型,但为了确保视图刷新,我们也可以重置代理模型) m_proxyModel->invalidate(); } else { QMessageBox::critical(this, "错误", "删除失败: " + query.lastError().text()); } } } ``` 4. 另外,我们还需要注意视图的选择模式。默认情况下,选择模式可能是选择单元格,我们需要设置为选择整行,以避免只选中一个单元格而导致获取整行数据时的问题。在构造函数中添加: ```cpp ui->tableView_change->setSelectionBehavior(QAbstractItemView::SelectRows); ``` 5. 同时,为了避免多选,可以设置选择模式为单选(如果需要多选删除,则需修改代码以处理多行): ```cpp ui->tableView_change->setSelectionMode(QAbstractItemView::SingleSelection); ``` 这样修改后,程序应该不会卡死,并且能够正确删除选中的行。 但是,重新加载整个模型可能在大数据量时效率不高。如果效率成为问题,可以考虑只删除模型中的行(如果模型支持)并提交到数据库,或者只刷新部分数据。但当前先以解决问题为主。 因此,完整的构造函数修改如下: 注意:在重新加载模型后,视图会更新,当前选择会丢失,这是可以接受的。 让我们重新整理一下代码: 头文件kchangemanage.h中: ```cpp #include <QDialog> #include "module/model/EquipmentDataModel.h" #include "module/model/filterproxymdel.h" namespace Ui { class kchangemanage; } class kchangemanage : public QDialog { Q_OBJECT public: explicit kchangemanage(QWidget *parent = nullptr); ~kchangemanage(); private: Ui::kchangemanage *ui; dataModel::EquipmentDataModel *m_sourceModel; // 声明 fliterModel::FilterProxyMdel *m_proxyModel; // 声明 private slots: void on_deleteButton_clicked(); void on_cancelButton_clicked(); }; ``` 源文件kchangemanage.cpp中: ```cpp kchangemanage::kchangemanage(QWidget *parent) : QDialog(parent), ui(new Ui::kchangemanage), m_sourceModel(nullptr), m_proxyModel(nullptr) // 初始化指针 { ui->setupUi(this); // 初始化模型 m_sourceModel = new dataModel::EquipmentDataModel(this); m_proxyModel = new fliterModel::FilterProxyMdel(this); m_proxyModel->setSourceModel(m_sourceModel); // 给视图指定模型 ui->tableView_change->setModel(m_proxyModel); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_ID); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_Count); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_RentId); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_ClassId); // 设置选择行为:按行选择 ui->tableView_change->setSelectionBehavior(QAbstractItemView::SelectRows); // 设置选择模式:单选(也可以是多选,根据需求) ui->tableView_change->setSelectionMode(QAbstractItemView::SingleSelection); // 调整列宽以显示数据 ui->tableView_change->resizeColumnsToContents(); ui->tableView_change->horizontalHeader()->setStretchLastSection(true); // 加载数据 m_sourceModel->fetchData(); // 调试信息 qDebug() << "Model row count:" << m_sourceModel->rowCount(); qDebug() << "Model column count:" << m_sourceModel->columnCount(); } // ... 析构函数不变 ... void kchangemanage::on_deleteButton_clicked() { // 修改后的删除函数(使用成员变量,并在删除后重新加载模型) QItemSelectionModel *selectionModel = ui->tableView_change->selectionModel(); if (!selectionModel->hasSelection()) { QMessageBox::warning(this, "警告", "请选择要删除的行!"); return; } // 获取选中的行(代理模型索引) QModelIndexList selected = selectionModel->selectedRows(); QModelIndex proxyIndex = selected.first(); // 映射到源模型索引 QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex); if (!sourceIndex.isValid()) { QMessageBox::critical(this, "错误", "无效的模型索引"); return; } // 获取主键ID int id = m_sourceModel->data( m_sourceModel->index(sourceIndex.row(), dataModel::EquipmentDataModel::Col_ID) ).toInt(); // 确认删除对话框 QMessageBox::StandardButton reply; reply = QMessageBox::question(this, "确认删除", "确定要删除选中的记录吗?", QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { QSqlQuery query; query.prepare("DELETE FROM equipment WHERE id = :id"); query.bindValue(":id", id); if (query.exec()) { QMessageBox::information(this, "成功", "删除成功!"); // 重新加载模型数据 m_sourceModel->fetchData(); // 由于重新加载了数据,代理模型需要重置(但fetchData会发出重置信号,所以视图会更新,这一步可以省略) // m_proxyModel->invalidate(); // 可以省略 } else { QMessageBox::critical(this, "错误", "删除失败: " + query.lastError().text()); } } } ``` 这样修改后,程序应该能够正常运行,不会卡死。 注意:如果EquipmentDataModel的fetchData函数在重新加载数据时没有正确更新模型,可能需要检查该函数的实现。另外,重新加载整个模型可能会重置视图的排序和选择状态,但这是目前最直接的方法。 如果仍然出现卡死,请检查EquipmentDataModel和FilterProxyMdel的实现,特别是数据加载和代理过滤的部分。 最后,确保在删除操作中,数据库操作是成功的,并且没有触发任何异常。 如果问题仍然存在,请提供EquipmentDataModel和FilterProxyMdel的代码,以便进一步分析。 现在,我们按照要求输出相关问题。</think>根据您提供的代码,程序在选中行执行删除后卡死的问题主要源于以下关键原因。以下是详细分析和修复方案: ### 问题根源分析 1. **未初始化的成员变量**: ```cpp // 删除函数中使用了未初始化的成员变量 QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex); int id = m_sourceModel->data(...).toInt(); ``` 但构造函数中创建的模型是局部变量: ```cpp dataModel::EquipmentDataModel *modelRent = new ...; // 局部变量 fliterModel::FilterProxyMdel *rentFilterProxyMdel = new ...; // 局部变量 ``` 2. **选择模式未设置**: ```cpp // 缺少关键设置导致选择行为异常 ui->tableView_change->setSelectionBehavior(QAbstractItemView::SelectRows); ``` 3. **数据库操作与模型更新冲突**: ```cpp // 直接执行SQL删除 query.exec("DELETE FROM equipment..."); // 然后尝试模型操作 m_sourceModel->removeRow(...); // 模型状态已不一致 ``` ### 完整修复方案 #### 1. 修改头文件(kchangemanage.h) ```cpp class kchangemanage : public QDialog { Q_OBJECT public: explicit kchangemanage(QWidget *parent = nullptr); ~kchangemanage(); private: Ui::kchangemanage *ui; // 添加成员变量声明 dataModel::EquipmentDataModel *m_sourceModel; fliterModel::FilterProxyMdel *m_proxyModel; private slots: void on_deleteButton_clicked(); void on_cancelButton_clicked(); }; ``` #### 2. 修改构造函数(kchangemanage.cpp) ```cpp kchangemanage::kchangemanage(QWidget *parent) : QDialog(parent), ui(new Ui::kchangemanage), m_sourceModel(nullptr), m_proxyModel(nullptr) // 初始化指针 { ui->setupUi(this); // 初始化模型并赋值给成员变量 m_sourceModel = new dataModel::EquipmentDataModel(this); m_proxyModel = new fliterModel::FilterProxyMdel(this); m_proxyModel->setSourceModel(m_sourceModel); // 给视图指定模型 ui->tableView_change->setModel(m_proxyModel); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_ID); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_Count); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_RentId); ui->tableView_change->hideColumn(dataModel::EquipmentDataModel::Col_ClassId); // 关键设置:选择整行模式(解决卡死的关键) ui->tableView_change->setSelectionBehavior(QAbstractItemView::SelectRows); ui->tableView_change->setSelectionMode(QAbstractItemView::SingleSelection); // 调整列宽 ui->tableView_change->resizeColumnsToContents(); ui->tableView_change->horizontalHeader()->setStretchLastSection(true); // 加载数据 m_sourceModel->fetchData(); // 调试信息 qDebug() << "Model row count:" << m_sourceModel->rowCount(); } ``` #### 3. 修复删除函数(关键修改) ```cpp void kchangemanage::on_deleteButton_clicked() { // 获取选择模型 QItemSelectionModel *selectionModel = ui->tableView_change->selectionModel(); // 检查是否有选中项 if (!selectionModel->hasSelection()) { QMessageBox::warning(this, "警告", "请选择要删除的行!"); return; } // 获取选中的第一行(代理模型索引) QModelIndexList selected = selectionModel->selectedRows(); if (selected.isEmpty()) return; QModelIndex proxyIndex = selected.first(); // 映射到源模型索引 QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex); if (!sourceIndex.isValid()) { QMessageBox::critical(this, "错误", "无效的模型索引"); return; } // 获取主键ID int id = m_sourceModel->data( m_sourceModel->index(sourceIndex.row(), dataModel::EquipmentDataModel::Col_ID) ).toInt(); // 确认删除 QMessageBox::StandardButton reply = QMessageBox::question( this, "确认删除", "确定要删除选中的记录吗?", QMessageBox::Yes | QMessageBox::No ); if (reply == QMessageBox::Yes) { // 方法1:通过模型删除(推荐) if (m_sourceModel->removeRow(sourceIndex.row())) { QMessageBox::information(this, "成功", "删除成功!"); // 刷新模型 m_sourceModel->fetchData(); } else { QMessageBox::critical(this, "错误", "删除失败: " + m_sourceModel->lastError().text()); } } } ``` ### 关键修改点说明 1. **成员变量初始化**: - 在构造函数中初始化`m_sourceModel`和`m_proxyModel` - 确保删除函数中访问的是有效指针 2. **选择模式设置**(解决卡死关键): ```cpp ui->tableView_change->setSelectionBehavior(QAbstractItemView::SelectRows); ui->tableView_change->setSelectionMode(QAbstractItemView::SingleSelection); ``` - 缺少这行会导致选择行为未定义,引发事件循环卡死 3. **统一的模型操作**: - 优先使用模型自身的`removeRow()`方法 - 确保数据库操作和模型状态同步 4. **安全访问检查**: - 添加了`isEmpty()`和`isValid()`检查 - 使用模型的内置错误处理 ### 高级优化建议 1. **添加事务支持**: ```cpp QSqlDatabase::database().transaction(); if (m_sourceModel->removeRow(sourceIndex.row())) { QSqlDatabase::database().commit(); m_sourceModel->fetchData(); // 刷新数据 } else { QSqlDatabase::database().rollback(); QMessageBox::critical(this, "错误", "删除失败"); } ``` 2. **异步删除处理**: ```cpp // 使用QtConcurrent避免UI冻结 QtConcurrent::run([=]() { if (m_sourceModel->removeRow(sourceIndex.row())) { QMetaObject::invokeMethod(this, [=]() { m_sourceModel->fetchData(); QMessageBox::information(this, "成功", "删除成功!"); }); } }); ``` 3. **添加删除动画反馈**: ```cpp // 显示删除动画 QPropertyAnimation *anim = new QPropertyAnimation(ui->tableView_change, "geometry"); anim->setDuration(300); anim->setStartValue(ui->tableView_change->geometry()); anim->setEndValue(QRect(ui->tableView_change->x(), ui->tableView_change->y(), 0, ui->tableView_change->height())); anim->start(QAbstractAnimation::DeleteWhenStopped); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值