如何从Xib文件加载自定义UITableViewCells?

从Xib加载自定义UITableViewCell
本文探讨了从Xib文件加载自定义UITableViewCell的各种方法,包括设置文件所有者、使用工厂类以及不同解决方案的优缺点。提供了代码示例,展示了如何在Xcode中设置和使用Xib文件。

本文翻译自:How do you load custom UITableViewCells from Xib files?

The question is simple: How do you load custom UITableViewCell from Xib files? 问题很简单:如何从Xib文件加载自定义UITableViewCell Doing so allows you to use Interface Builder to design your cells. 这样做允许您使用Interface Builder来设计单元格。 The answer apparently is not simple due to memory managment issues. 由于内存管理问题,答案显然并不简单。 This thread mentions the issue and suggests a solution, but is pre NDA-release and lacks code. 这个主题提到了这个问题并建议了一个解决方案,但是在NDA发布之前并且没有代码。 Here's a long thread that discusses the issue without providing a definitive answer. 这是一个很长的线程 ,在没有提供明确答案的情况下讨论了这个问题。

Here's some code I've used: 这是我用过的一些代码:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

To use this code, create MyCell.m/.h, a new subclass of UITableViewCell and add IBOutlets for the components you want. 要使用此代码,请创建MyCell.m / .h, UITableViewCell的新子类,并为所需的组件添加IBOutlets Then create a new "Empty XIB" file. 然后创建一个新的“空XIB”文件。 Open the Xib file in IB, add a UITableViewCell object, set its identifier to "MyCellIdentifier", and set its class to MyCell and add your components. 在IB中打开Xib文件,添加UITableViewCell对象,将其标识符设置为“MyCellIdentifier”,并将其类设置为MyCell并添加组件。 Finally, connect the IBOutlets to the components. 最后,将IBOutlets连接到组件。 Note that we did not set the File's Owner in IB. 请注意,我们没有在IB中设置文件所有者。

Other methods advocate setting the File's Owner and warn of memory leaks if the Xib is not loaded via an additional factory class. 如果未通过其他工厂类加载Xib,则其他方法主张设置文件所有者并警告内存泄漏。 I tested the above under Instruments/Leaks and saw no memory leaks. 我在Instruments / Leaks下测试了上面的内容,没有发现内存泄漏。

So what's the canonical way to load cells from Xibs? 那么从Xibs加载单元格的规范方法是什么? Do we set File's Owner? 我们设置文件的所有者吗? Do we need a factory? 我们需要工厂吗? If so, what's the code for the factory look like? 如果是这样,工厂的代码是什么样的? If there are multiple solutions, let's clarify the pros and cons of each of them... 如果有多种解决方案,让我们澄清每种解决方案的优缺点......


#1楼

参考:https://stackoom.com/question/2gzf/如何从Xib文件加载自定义UITableViewCells


#2楼

The correct way to do it is to create a UITableViewCell subclass implementation, header, and XIB. 正确的方法是创建UITableViewCell子类实现,标头和XIB。 In the XIB remove any views and just add a table cell. 在XIB中删除任何视图,只需添加一个表格单元格。 Set the class as the name of the UITableViewCell subclass. 将类设置为UITableViewCell子类的名称。 For file owner, make it the UITableViewController subclass class name. 对于文件所有者,使其成为UITableViewController子类的类名。 Connect the file owner to the cell using the tableViewCell outlet. 使用tableViewCell插座将文件所有者连接到单元。

In the header file: 在头文件中:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

In the implementation file: 在实现文件中:

@synthesize tableViewCell = _tableViewCell;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *kCellIdentifier = @"reusableCell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}

#3楼

The right solution is this: 正确的解决方案是:

- (void)viewDidLoad
{
    [super viewDidLoad];
    UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of ItemCell
    PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    return cell;
}

#4楼

Check this - http://eppz.eu/blog/custom-uitableview-cell/ - really convenient way using a tiny class that ends up one line in controller implementation: 检查一下 - http://eppz.eu/blog/custom-uitableview-cell/ - 使用一个在控制器实现中结束一行的小类非常方便的方法:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}

在此输入图像描述


#5楼

I've decided to post since I don't like any of these answers -- things can always be more simple and this is by far the most concise way I've found. 我决定发帖,因为我不喜欢任何这些答案 - 事情总是更简单,这是迄今为止我发现的最简洁的方式。

1. Build your Xib in Interface Builder as you like it 1.根据需要在Interface Builder中构建Xib

  • Set File's Owner to class NSObject 将File的Owner设置为NSObject类
  • Add a UITableViewCell and set its class to MyTableViewCellSubclass -- if your IB crashes (happens in Xcode > 4 as of this writing), just use a UIView of do the interface in Xcode 4 if you still have it laying around 添加一个UITableViewCell并将其类设置为MyTableViewCellSubclass - 如果您的IB崩溃(在撰写本文时Xcode> 4中发生),只需使用UIView在Xcode 4中执行该接口,如果您仍然存在它
  • Layout your subviews inside this cell and attach your IBOutlet connections to your @interface in the .h or .m (.m is my preference) 在此单元格内布置子视图,并将您的IBOutlet连接附加到.h或.m中的@interface(.m是我的偏好)

2. In your UIViewController or UITableViewController subclass 2.在UIViewController或UITableViewController子类中

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. In your MyTableViewCellSubclass 3.在MyTableViewCellSubclass中

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}

#6楼

Here are two methods which the original author states was recommended by an IB engineer . 以下是IB工程师推荐的两种原始作者所述的方法。

See the actual post for more details. 有关详细信息,请参阅实际帖子。 I prefer method #2 as it seems simpler. 我更喜欢方法#2,因为它似乎更简单。

Method #1: 方法#1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

Method #2: 方法#2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Update (2014): Method #2 is still valid but there is no documentation for it anymore. 更新(2014):方法#2仍然有效,但不再有文档。 It used to be in the official docs but is now removed in favor of storyboards. 它曾经在官方文档中,但现在被删除,有利于故事板。

I posted a working example on Github: 我在Github上发布了一个工作示例:
https://github.com/bentford/NibTableCellExample https://github.com/bentford/NibTableCellExample

edit for Swift 4.2 编辑Swift 4.2

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}
<think>我们正在讨论Gemini GenAI SDK中的聊天历史功能。由于这是一个特定的技术问题,我将根据一般性的知识和常见的SDK设计模式来回答,因为Gemini GenAI SDK的具体实现细节可能随时间变化,且我没有实时的访问权限。但我会尽量提供通用且可靠的指导。 聊天历史功能通常是指在一个持续对话中,模型能够记住之前的对话内容,从而在后续的回复中保持上下文。在Gemini GenAI SDK中,实现聊天历史管理通常涉及以下几个关键点: 1. **消息传递结构**:通常,每次调用模型时,我们需要传递整个对话历史(包括用户的消息和模型的回复)作为上下文。这样模型才能基于整个对话历史生成回复。 2. **消息格式**:每条消息通常包含角色(如"user"或"model")和内容(文本)。例如: ```json [ {"role": "user", "content": "你好"}, {"role": "model", "content": "你好!有什么可以帮助你的吗?"}, {"role": "user", "content": "告诉我Gemini是什么?"} ] ``` 3. **管理对话历史**:开发者需要维护一个消息列表,每次用户发送新消息时,将新消息添加到历史列表中,然后调用模型生成回复,再将模型的回复也添加到历史列表中。注意,历史记录的长度不能超过模型的最大上下文长度(通常有token限制)。 4. **截断历史记录**:当对话历史很长时,需要截断以避免超过token限制。常见的策略是保留最近的几条消息,或者删除中间的一些消息但保留开头(如系统指令)和最近的对话。 5. **Gemini SDK中的具体实现**:在Gemini SDK中,可能会有一个`ChatSession`类来管理对话状态。例如: ```python import google.generativeai as genai genai.configure(api_key="YOUR_API_KEY") model = genai.GenerativeModel(&#39;gemini-pro&#39;) chat = model.start_chat(history=[]) # 初始历史为空 response = chat.send_message("你好") print(response.text) # 模型的回复 # 继续对话 response = chat.send_message("告诉我Gemini是什么?") print(response.text) # 查看历史 for message in chat.history: print(message.role, message.parts[0].text) ``` 在上面的例子中,`chat`对象会自动维护历史记录。每次调用`send_message`后,用户的消息和模型的回复都会被添加到`chat.history`中。 6. **常见问题**: - **上下文丢失**:如果发现模型没有记住之前的对话,请检查是否在每次发送消息时都使用了同一个`ChatSession`实例,并且历史记录是否正确传递。 - **token限制**:当历史记录很长时,可能会超过模型的最大token限制(例如,gemini-pro模型的最大上下文长度是30720个token)。此时,需要手动截断历史记录(如保留最近的几条消息)或者使用更高级的摘要技术。 - **历史记录存储**:如果需要长期存储对话,可以将历史记录保存到数据库,并在下次对话时重新加载。但要注意,重新加载后,需要确保整个历史记录不超过token限制。 7. **重置历史**:如果需要开始一个新的对话,可以创建一个新的`ChatSession`实例。 8. **系统指令**:在对话开始时,可以设置一个系统指令(system instruction)来指导模型的行为。例如: ```python model = genai.GenerativeModel(&#39;gemini-pro&#39;, system_instruction="你是一个专业的客服助手。") ``` 以上是关于Gemini GenAI SDK中聊天历史功能的基本实现和常见问题处理。具体细节请参考官方文档。 由于这是一个技术性较强的问题,我生成的相关问题将围绕使用中的常见场景和问题: §§相关问题§§ 1. 如何在Gemini GenAI SDK中重置聊天历史? 2. 当对话历史很长时,如何避免超过模型的token限制? 3. 如何将聊天历史保存到数据库并在下次对话时恢复? 4. Gemini模型支持的最大上下文长度是多少? 5. 在Gemini SDK中,如何设置系统指令来指导模型的行为?</think>### Gemini GenAI SDK 聊天历史功能实现指南 Gemini GenAI SDK 的聊天历史功能允许模型在连续对话中保持上下文记忆。以下是关键实现步骤和常见问题解决方案: #### 一、核心实现方法 1. **历史记录传递** 每次请求需将完整对话历史作为上下文传入: ```python from google.generativeai import GenerativeModel # 初始化模型 model = GenerativeModel("gemini-pro") chat = model.start_chat(history=[]) # 初始历史为空 # 首次交互 response = chat.send_message("解释量子力学") print(response.text) # 模型回复 # 后续交互(自动携带历史) response = chat.send_message("用简单例子说明") print(response.text) # 基于上下文的回复 ``` 2. **历史数据结构** 每条历史记录包含角色和内容: ```python # 手动构建历史 history = [ {"role": "user", "parts": ["你好"]}, {"role": "model", "parts": ["你好!有什么可以帮助你?"]} ] ``` 3. **历史管理技巧** - **上下文长度限制**:Gemini 1.5 Pro 支持最大 1M token 上下文[^1] - **历史截断策略**: ```python # 保留最近10轮对话 if len(chat.history) > 20: # 10轮对话=20条消息 chat.history = chat.history[-20:] ``` #### 二、常见问题解决 1. **上下文丢失问题** **症状**:模型忘记之前的对话 **解决方案**: - 检查是否每次调用都传递了完整 `chat.history` - 验证 token 计数是否超限: ```python import tiktoken encoder = tiktoken.encoding_for_model("gpt-4") tokens = encoder.encode(str(chat.history)) print(len(tokens)) # 需小于模型上限 ``` 2. **历史记录污染** **场景**:用户输入包含敏感数据 **清理方法**: ```python def clean_history(history): return [msg for msg in history if not is_sensitive(msg["parts"][0])] ``` 3. **性能优化** - 使用 `stream=True` 流式传输减少延迟 - 对长历史启用摘要功能: ```python summary = model.generate_content("总结对话要点: " + str(history[-5:])) new_history = [{"role": "system", "parts": [summary.text]}] ``` #### 三、最佳实践 1. **混合历史策略** ```mermaid graph LR A[新对话] --> B{历史长度>阈值?} B -->|是| C[摘要前5轮+完整后5轮] B -->|否| D[使用完整历史] ``` 2. **元数据存储** 建议将历史记录与会话元数据(如 session_id)共同存储: ```python # Firebase 存储示例 firestore.collection("chats").document(session_id).set({ "history": chat.history, "last_updated": firestore.SERVER_TIMESTAMP }) ``` > **重要提示**:Gemini 1.5 支持原生文件处理(PDF/Word),可将文档作为历史上下文传入[^1]: > ```python > response = chat.send_message( > "总结此文档", > file=open("report.pdf", "rb") > ) > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值