Skip to main content

(Optional) Read More

Backgroud

"Read More" is a special integration option for the Outbrain Bridge widget that allows users to immediately access recommended content when they open an article screen. With this feature, the article content is displayed in the top two-thirds of the screen, while the bottom third contains the Outbrain Bridge widget.

This allows users to seamlessly transition from reading the article to discovering additional content recommended by Outbrain.

Read More iOS TableView

UITableView

In order to implement "Read More" in UITableView, publisher should basically do the following:

Step 1

Divide the "article screen" into 2 sections.

Step 2

Use READ_MORE_FLAG_IS_ACTIVE flag to distinguish between 2 states (whether to display the entire content of the article screen or just a fraction of it, to leave place for the widget in the viewport).

Step 3

Place Outbrain Bridge widget (SFWidget) in the 2nd "section", and the article content in the 1st section.

Step 4

In numberOfRowsInSection method you should check if READ_MORE_FLAG_IS_ACTIVE is true, if so, return only the first X items in section 0 (instead of the entire screen content). Your goal is to have the section short enough so that the section below (Outbrain) will be visible when the users open the screen.

Example for `numberOfRowsInSection``

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == OUTBRAIN_SECTION_INDEX) {
return 1
}
else {
return READ_MORE_FLAG_IS_ACTIVE ? (originalArticleItemsCount - 3) : originalArticleItemsCount
}
}

Step 5

The last cell in section 0 (article content) should contain the "Read More" button, therefore make sure to call a method addReadMoreOverlayToCell() to add the "Read More" button with some 'semi-transparent overlay' on top of the cell contentView.

This is just a very basic (and not optimal) example of how this can be done:

func addReadMoreOverlayToCell(_ cell: UITableViewCell) {
let customView = UIView(frame: CGRect(x: 0, y: cell.contentView.frame.height - 100, width: cell.contentView.frame.width + 20, height: 100))
customView.backgroundColor = UIColor.white.withAlphaComponent(0.8)
customView.tag = READ_MORE_OVERLAY_TAG

// Add "Read More" button
let button = UIButton(type: .system)
button.tag = READ_MORE_BUTTON_TAG
button.backgroundColor = UIColor.white
button.frame = CGRect(x: 100, y: cell.contentView.frame.height - 80, width: cell.contentView.frame.width - 200, height: 40)
button.setTitle("Read More", for: .normal)
button.addTarget(self, action: #selector(readMoreButtonTapped(_:)), for: .touchUpInside)
button.layer.borderWidth = 1.0
button.layer.borderColor = UIColor.blue.cgColor

button.layer.cornerRadius = 5.0

cell.contentView.tag = READ_MORE_CELL_CONTENT_VIEW_TAG
cell.contentView.addSubview(customView)
cell.contentView.addSubview(button)
}

Step 6

When the user "clicks" on the "Read More" button, we should do the following:

  • Change the flag READ_MORE_FLAG_IS_ACTIVE to false
  • Remove the "Read More" button and overlay from the cell.
  • Call reloadTable

An example for readMoreButtonTapped

@objc func readMoreButtonTapped(_ sender: UIButton) {
// Handle button tap event
print("Read More button tapped")
READ_MORE_FLAG_IS_ACTIVE = false
var parentView = sender.superview
while (parentView!.tag != READ_MORE_CELL_CONTENT_VIEW_TAG) {
parentView = parentView?.superview
}
if let customView = parentView!.viewWithTag(READ_MORE_OVERLAY_TAG),
let button = parentView!.viewWithTag(READ_MORE_BUTTON_TAG) {
customView.removeFromSuperview()
button.removeFromSuperview()
}

tableView.reloadData()
}

Complete Example file

import UIKit
import WebKit
import SafariServices
import OutbrainSDK

class TableVC : UIViewController, UITableViewDelegate, UITableViewDataSource, OBViewController {
var widgetId: String = OBConf.smartFeedWidgetID

@IBOutlet weak var tableView: UITableView!

let imageHeaderCellReuseIdentifier = "imageHeaderCell"
let textHeaderCellReuseIdentifier = "textHeaderCell"
let contentCellReuseIdentifier = "contentHeaderCell"
let READ_MORE_CELL_CONTENT_VIEW_TAG = 1111
let READ_MORE_BUTTON_TAG = 1112
let READ_MORE_OVERLAY_TAG = 1113
var sfWidget: SFWidget!

let originalArticleItemsCount = 6
var READ_MORE_FLAG_IS_ACTIVE = true
let OUTBRAIN_SECTION_INDEX = 1

override func viewDidLoad() {
super.viewDidLoad()

tableView.delegate = self
tableView.dataSource = self

tableView.register(SFWidgetTableCell.self, forCellReuseIdentifier: "SFWidgetCell")

sfWidget = SFWidget(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 0))

self.sfWidget.configure(with: self, url: OBConf.baseURL, widgetId: self.widgetId, installationKey: OBConf.installationKey)
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
self.sfWidget.viewWillTransition(to: size, with: coordinator)
}

// MARK: UITableView methods

func numberOfSections(in tableView: UITableView) -> Int {
return 2
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (section == OUTBRAIN_SECTION_INDEX) {
return 1
}
else {
return READ_MORE_FLAG_IS_ACTIVE ? (originalArticleItemsCount - 3) : originalArticleItemsCount
}
}

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

if (indexPath.section == OUTBRAIN_SECTION_INDEX) {
if let sfWidgetCell = self.tableView.dequeueReusableCell(withIdentifier: "SFWidgetCell") as? SFWidgetTableCell {
return sfWidgetCell
}
}
switch indexPath.row {
case 0:
cell = self.tableView.dequeueReusableCell(withIdentifier: imageHeaderCellReuseIdentifier) as UITableViewCell?
case 1:
cell = self.tableView.dequeueReusableCell(withIdentifier: textHeaderCellReuseIdentifier) as UITableViewCell?
default:
// Customize cell.contentView
cell = self.tableView.dequeueReusableCell(withIdentifier: contentCellReuseIdentifier) as UITableViewCell?
if (READ_MORE_FLAG_IS_ACTIVE) {
addReadMoreOverlayToCell(cell!)
}
break;
}
return cell ?? UITableViewCell()
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if (indexPath.section == OUTBRAIN_SECTION_INDEX) {
if let sfWidgetCell = cell as? SFWidgetTableCell {
self.sfWidget.willDisplay(sfWidgetCell)
}
return
}
switch indexPath.row {
case 0:
break
case 1:
if let articleCell = cell as? AppArticleTableViewCell {
articleCell.backgroundColor = UIColor.white
let fontSize = UIDevice.current.userInterfaceIdiom == .pad ? 30.0 : 20.0
articleCell.headerLabel.font = UIFont(name: articleCell.headerLabel.font!.fontName, size: CGFloat(fontSize))
articleCell.headerLabel.textColor = UIColor.black
}
break
default:
if let articleCell = cell as? AppArticleTableViewCell {
articleCell.backgroundColor = UIColor.white
let fontSize = UIDevice.current.userInterfaceIdiom == .pad ? 20.0 : 15.0
articleCell.contentTextView.font = UIFont(name: articleCell.contentTextView.font!.fontName, size: CGFloat(fontSize))
articleCell.contentTextView.textColor = UIColor.black
}
break
}
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.section == OUTBRAIN_SECTION_INDEX) {
return self.sfWidget.getCurrentHeight();
}
switch indexPath.row {
case 0:
return UIDevice.current.userInterfaceIdiom == .pad ? 400 : 250;
case 1:
return UIDevice.current.userInterfaceIdiom == .pad ? 150 : UITableView.automaticDimension;
default:
return UIDevice.current.userInterfaceIdiom == .pad ? 200 : UITableView.automaticDimension;
}
}

@objc func readMoreButtonTapped(_ sender: UIButton) {
// Handle button tap event
print("Read More button tapped")
READ_MORE_FLAG_IS_ACTIVE = false
var parentView = sender.superview
while (parentView!.tag != READ_MORE_CELL_CONTENT_VIEW_TAG) {
parentView = parentView?.superview
}
if let customView = parentView!.viewWithTag(READ_MORE_OVERLAY_TAG),
let button = parentView!.viewWithTag(READ_MORE_BUTTON_TAG) {
customView.removeFromSuperview()
button.removeFromSuperview()
}

tableView.reloadData()
}

func addReadMoreOverlayToCell(_ cell: UITableViewCell) {
let customView = UIView(frame: CGRect(x: 0, y: cell.contentView.frame.height - 100, width: cell.contentView.frame.width + 20, height: 100))
customView.backgroundColor = UIColor.white.withAlphaComponent(0.8)
customView.tag = READ_MORE_OVERLAY_TAG

// Add "Read More" button
let button = UIButton(type: .system)
button.tag = READ_MORE_BUTTON_TAG
button.backgroundColor = UIColor.white
button.frame = CGRect(x: 100, y: cell.contentView.frame.height - 80, width: cell.contentView.frame.width - 200, height: 40)
button.setTitle("Read More", for: .normal)
button.addTarget(self, action: #selector(readMoreButtonTapped(_:)), for: .touchUpInside)
button.layer.borderWidth = 1.0
button.layer.borderColor = UIColor.blue.cgColor

button.layer.cornerRadius = 5.0

cell.contentView.tag = READ_MORE_CELL_CONTENT_VIEW_TAG
cell.contentView.addSubview(customView)
cell.contentView.addSubview(button)
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
sfWidget.scrollViewDidScroll(scrollView)
}
}


// MARK: SFWidgetDelegate
extension TableVC: SFWidgetDelegate {
func didChangeHeight() {
tableView.beginUpdates()
tableView.endUpdates()
}

func onOrganicRecClick(_ url: URL) {
// handle click on organic url
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}

func onRecClick(_ url: URL) {
let safariVC = SFSafariViewController(url: url)
self.navigationController?.present(safariVC, animated: true, completion: nil)
}
}